rchart 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/examples/logo.png ADDED
Binary file
@@ -0,0 +1,5 @@
1
+ 168,188,56
2
+ 188,208,76
3
+ 208,228,96
4
+ 228,245,116
5
+ 248,255,136
Binary file
Binary file
Binary file
Binary file
data/fonts/tahoma.ttf ADDED
Binary file
data/lib/rchart.rb ADDED
@@ -0,0 +1,3270 @@
1
+ require "GD"
2
+ require 'rdata'
3
+ require 'version'
4
+ class Rchart
5
+ SCALE_NORMAL = 1
6
+ SCALE_ADDALL = 2
7
+ SCALE_START0 = 3
8
+ SCALE_ADDALLSTART0 = 4
9
+ PIE_PERCENTAGE = 1
10
+ PIE_LABELS = 2
11
+ PIE_NOLABEL = 3
12
+ PIE_PERCENTAGE_LABEL = 4
13
+ TARGET_GRAPHAREA = 1
14
+ TARGET_BACKGROUND = 2
15
+ ALIGN_TOP_LEFT = 1
16
+ ALIGN_TOP_CENTER = 2
17
+ ALIGN_TOP_RIGHT = 3
18
+ ALIGN_LEFT = 4
19
+ ALIGN_CENTER = 5
20
+ ALIGN_RIGHT = 6
21
+ ALIGN_BOTTOM_LEFT = 7
22
+ ALIGN_BOTTOM_CENTER = 8
23
+ ALIGN_BOTTOM_RIGHT = 9
24
+ FONT_PATH = Version.font_path
25
+ attr_accessor :antialias_quality
26
+ # This function create a new chart object.
27
+ # This object will be used during all the steps of the graph creation.
28
+ # This object will embed all the pChart functions.
29
+
30
+ def initialize(x_size,y_size,options={})
31
+ # Initialize variables
32
+ # q raise ArgumentError if (options[:x_size].nil? && options[:y_size].nil?)
33
+ # Error management
34
+ @error_reporting = false
35
+ @error_interface = "cli"
36
+ @errors = []
37
+ @error_font_name = "#{FONT_PATH}/pf_arma_five.ttf"
38
+ @error_font_size = 6
39
+ @x_size = x_size
40
+ @antialias_quality=0
41
+ @y_size = y_size
42
+ @error_reporting = false
43
+ @error_font_name = "#{FONT_PATH}/pf_arma_five.ttf"
44
+ @error_font_size = 6
45
+ @currency = "Rs."
46
+ @date_format = "%d/%m/%Y"
47
+ @line_width = 1
48
+ @line_dot_size = 0
49
+ @anti_alias_quality = 0
50
+ @shadow_active = false
51
+ @shadow_x_distance = 1
52
+ @shadow_y_distance = 1
53
+ @shadow_r_color = 60
54
+ @shadow_g_color = 60
55
+ @shadow_b_color = 60
56
+ @shadow_alpha = 50
57
+ @shadow_blur = 0
58
+ @tmp_dir = '/tmp'
59
+ @font_size =8
60
+ @font_name = "#{FONT_PATH}/tahoma.ttf"
61
+ @divisions= 0
62
+
63
+ @division_count = 0
64
+ @division_height = 0
65
+ @x_division_ratio =0
66
+ @x_division_count = 0
67
+ @x_division_height = 0
68
+ @x_division_ratio = 0
69
+ @palette = []
70
+ @layers = []
71
+ @g_area_x_offset =0
72
+ @division_width = 0
73
+ @vmin = nil
74
+ @vmax = nil
75
+ @v_x_min =nil
76
+ @v_x_max=nil
77
+
78
+ @x_divisions=0
79
+ @data_count=nil
80
+ @g_area_x1 = 0
81
+ @g_area_y1 = 0
82
+ @g_area_x2 = 0
83
+ @g_area_y2 = 0
84
+ @image_map = []
85
+ # /* Image Map settings */
86
+ @build_map=false
87
+ @map_function = nil
88
+ @tmp_folder = "tmp/"
89
+ @map_id = nil
90
+
91
+ @palette =[{"r"=>188,"g"=>224,"b"=>46},
92
+ {"r"=>224,"g"=>100,"b"=>46},
93
+ {"r"=>224,"g"=>214,"b"=>46},
94
+ {"r"=>46,"g"=>151,"b"=>224},
95
+ {"r"=>176,"g"=>46,"b"=>224},
96
+ {"r"=>224,"g"=>46,"b"=>117},
97
+ {"r"=>92,"g"=>224,"b"=>46},
98
+ {"r"=>224,"g"=>176,"b"=>46}]
99
+ @picture = GD::Image.newTrueColor(@x_size, @y_size)
100
+ @c_white = @picture.colorAllocate(255,255,255)
101
+ image_filled_rectangle(@picture, 0, 0, @x_size, @y_size, 255,255,255)
102
+ #image_color_transparent(@picture, 255,255,255)
103
+ self.set_font_properties("tahoma.ttf",8)
104
+ end
105
+ # Use this function to enable error reporting during the chart rendering.
106
+ # By default messages are redirected to the console while using the render command and using GD while using the stroke command.
107
+ # You can force the errors to be redirected to either cli or gd specifying it as parameter.
108
+ def report_warnings(interface="cli")
109
+ @error_reporting = true
110
+ @error_interface = interface
111
+ end
112
+
113
+ # Set font Properties font_name,font_size
114
+ # font_name is
115
+ # * GeosansLight.ttf,
116
+ # * MankSans.ttf,
117
+ # * pf_arma_five.ttf,
118
+ # * Silkscreen.ttf,
119
+ # * tahoma.ttf
120
+ def set_font_properties(font_name, font_size)
121
+ @font_size = font_size
122
+ @font_name = "#{FONT_PATH}/#{font_name}"
123
+ end
124
+
125
+
126
+ #Use this function to set shadow properties.
127
+ def set_shadow_properties(x_distance=1,y_distance=1,r=60,g=60,b=60,alpha=50,blur=0)
128
+ @shadow_active = true
129
+ @shadow_x_distance = x_distance
130
+ @shadow_y_distance = y_distance
131
+ @shadow_r_color = r
132
+ @shadow_g_color = g
133
+ @shadow_b_color = b
134
+ @shadow_alpha = alpha
135
+ @shadow_blur = blur
136
+ end
137
+
138
+ # Use this function to deactivate the shadow options.
139
+ # Drawing shadows is time and CPU intensive.
140
+ def clear_shadow
141
+ @shadow_active = false
142
+ end
143
+
144
+ def validate_color(b, g, r)
145
+ r = 0 if ( r < 0 )
146
+ r = 255 if ( r > 255 )
147
+ g = 0 if ( g < 0 )
148
+ g = 255 if ( g > 255 )
149
+ b = 0 if ( b < 0 )
150
+ b = 255 if ( b > 255 )
151
+ return b, g, r
152
+ end
153
+
154
+ # This function can be used to change the color of one series.
155
+ # series id are starting at 0 for associated data serie #1.
156
+ # You must provide an rgb color.
157
+ def set_color_palette(id,r,g,b)
158
+ b,g,r=validate_color(b, g, r)
159
+ @palette[id]["r"] = r
160
+ @palette[id]["g"] = g
161
+ @palette[id]["b"] = b
162
+ end
163
+
164
+ # Create a color palette shading from one color to another
165
+ # This function will fill the color palette with 10 shades between the two RGB colors 0,0,0 and 100,100,100.This will produce grey shades. (Palette id 0-9 will be filled)
166
+ def create_color_gradient_palette(r1,g1,b1,r2,g2,b2,shades)
167
+ r_factor = (r2-r1)/shades
168
+ g_factor = (g2-g1)/shades
169
+ b_factor = (b2-b1)/shades
170
+ i= 0
171
+ while(i<= shades-1)
172
+ @palette[i]["r"] = r1+r_factor*i
173
+ @palette[i]["g"] = g1+g_factor*i
174
+ @palette[i]["b"] = b1+b_factor*i
175
+ i = i+1
176
+ end
177
+ end
178
+
179
+ # This function will load the color scheme from a text file.
180
+ # This file must be formated with three values per line ( r,g,b ).
181
+ # By default the delimiter is a coma but you can specify it.
182
+ def load_color_palette_from_file(file_name)
183
+ color_id = 0
184
+ File.open(file_name,"r") do |infile|
185
+ while (line = infile.gets)
186
+ values = line.split(",")
187
+ if ( values.length == 3 )
188
+ @palette[color_id]["r"] = values[0].to_i
189
+ @palette[color_id]["g"] = values[1].to_i
190
+ @palette[color_id]["b"] = values[2].to_i
191
+ color_id+=1
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ # Load palette from array [[r,g,b],[r1,g1,b1]]
198
+ def load_color_palette(color_palette)
199
+ color_id = 0
200
+ color_palette.each do |palette|
201
+ if palette.length == 3
202
+ @palette[color_id]["r"] = palette[0].to_i
203
+ @palette[color_id]["g"] = palette[1].to_i
204
+ @palette[color_id]["b"] = palette[2].to_i
205
+ color_id+=1
206
+ end
207
+ end
208
+ end
209
+
210
+ # This function allow you to customise the way lines are drawn in charts.
211
+ # This function only applies during chart drawing calls ( line charts,.. ).
212
+ # You can specify the width of the lines & if they are dotted.
213
+ def set_line_style(width=1,dot_size=0)
214
+ @line_width = width
215
+ @line_dot_size = dot_size
216
+ end
217
+
218
+ # Set currency symbol
219
+ def set_currency(currency)
220
+ @currency = currency
221
+ end
222
+
223
+ # A call to this function is mandatory when creating a graph.
224
+ # The upper left and bottom right border positions are used as arguments.
225
+ # This area will be used to draw graphs, grid, axis & more.
226
+ # Calling this function will not draw anything this will only set the graph area boundaries.
227
+ def set_graph_area(x1,y1,x2,y2)
228
+ @g_area_x1 = x1
229
+ @g_area_y1 = y1
230
+ @g_area_x2 = x2
231
+ @g_area_y2 = y2
232
+ end
233
+
234
+ # Prepare the graph area
235
+ def draw_graph_area(r,g,b,stripe=false)
236
+ self.draw_filled_rectangle(@g_area_x1,@g_area_y1,@g_area_x2,@g_area_y2,r,g,b,false)
237
+ self.draw_rectangle(@g_area_x1,@g_area_y1,@g_area_x2,@g_area_y2,r-40,g-40,b-40)
238
+ i=0
239
+ if stripe
240
+ r2 = r-15
241
+ r2 = 0 if r2<0
242
+ g2 = r-15
243
+ g2 = 0 if g2 < 0
244
+ b2 = r-15
245
+ b2 = 0 if b2 < 0
246
+ line_color = allocate_color(@picture,r2,g2,b2)
247
+ skew_width = @g_area_y2-@g_area_y1-1
248
+
249
+ i = @g_area_x1-skew_width
250
+
251
+ while i.to_f<=@g_area_x2.to_f
252
+ x1 = i
253
+ y1 = @g_area_y2
254
+ x2 = i+skew_width
255
+ y2 = @g_area_y1
256
+ if ( x1 < @g_area_x1 )
257
+ x1 = @g_area_x1
258
+ y1 = @g_area_y1 + x2 - @g_area_x1 + 1
259
+ end
260
+ if ( x2 >= @g_area_x2 )
261
+ y2 = @g_area_y1 + x2 - @g_area_x2 +1
262
+ x2 = @g_area_x2 - 1
263
+ end
264
+ image_line(@picture,x1,y1,x2,y2+1,r2,g2,b2)
265
+ i = i+4
266
+ end
267
+
268
+ end
269
+ end
270
+
271
+ # Allow you to clear the scale : used if drawing multiple charts
272
+ # You'll need to call this function only if you're planning to draw a second chart in the rendered picture.
273
+ # Calling this function will clear the current scaling parameters thus you'll need to call again the draw_scale function before drawing any new chart.
274
+ def clear_scale
275
+ @vmin = nil
276
+ @vmax = nil
277
+ @v_x_min = nil
278
+ @v_x_max = nil
279
+ @divisions = 0
280
+ @x_divisions = 0
281
+ end
282
+
283
+ # Allow you to fix the scale, use this to bypass the automatic scaling
284
+ # You can use this function to skip the automatic scaling.
285
+ # vmin and vmax will be used to render the graph.
286
+ def set_fixed_scale(v_min,v_max,divisions=5,v_x_min=0,v_x_max=0,x_divisions=5)
287
+ @vmin = v_min.to_f
288
+ @vmax = v_max.to_f
289
+ @divisions = divisions.to_f
290
+
291
+ if (!v_x_min == 0 )
292
+ @v_x_min = v_x_min.to_f
293
+ @v_x_max = v_x_max.to_f
294
+ @x_divisions = x_divisions.to_f
295
+ end
296
+ end
297
+
298
+ # Wrapper to the draw_scale function allowing a second scale to be drawn
299
+ # It takes the same parameters of the draw_scale function.
300
+ # The scale values will be written on the right side of the graph area.
301
+ def draw_right_scale(data,data_description,scale_mode,r,g,b,draw_ticks=true,angle=0,decimals=1,with_margin=false,skip_labels=1)
302
+ self. draw_scale(data, data_description, scale_mode, r, g, b,draw_ticks,angle,decimals,with_margin,skip_labels,true)
303
+ end
304
+
305
+ # This function will draw both axis and write values on it. You can disable the labelling of the axis setting draw_ticks to false. angle can be used to rotate the vertical ticks labels.
306
+ # decimal specify the number of decimal values we want to keep. Setting draw_ticks to false will not draw vertical & horizontal ticks on the axis ( labels will also not be written ).
307
+ # There is four way of computing scales :
308
+ # * Getting Max & Min values per serie : scale_mode = Rchart::SCALE_NORMAL
309
+ # * Like the previous one but setting the min value to 0 : scale_mode = Rchart::SCALE_START0
310
+ # * Getting the series cumulative Max & Min values : scale_mode = Rchart::SCALE_ADDALL
311
+ # * Like the previous one but setting the min value to 0 : scale_mode = Rchart::SCALE_ADDALLSTART0
312
+ # This will depends on the kind of graph you are drawing, Drawing graphs were you want to fix the min value to 0 you must use the Rchart::SCALE_START0 option.
313
+ # You can display only one x label every xi labels using the skip_labels parameter.
314
+ # Keeping with_margin to false will make the chart use all the width of the graph area. For most graphs the rendering will be better. In some circumstances you'll have to set it to true ( introducing left & right margin ) : bar charts will require it.
315
+ def draw_scale(data,data_description,scale_mode,r,g,b,draw_ticks=true,angle=0,decimals=1,with_margin=false,skip_labels=1,right_scale=false)
316
+ # Validate the Data and DataDescription array
317
+ data = self.validate_data("draw_scale",data)
318
+ c_text_color = allocate_color(@picture,r,g,b)
319
+ self.draw_line(@g_area_x1,@g_area_y1,@g_area_x1,@g_area_y2,r,g,b)
320
+ self.draw_line(@g_area_x1,@g_area_y2,@g_area_x2,@g_area_y2,r,g,b)
321
+ scale =0
322
+ divisions =0
323
+ if(@vmin.nil? && @vmax.nil?)
324
+ if (!data_description["values"][0].nil?)
325
+ #My hack TODO for LINE GRAPH
326
+ if data_description["values"].is_a?(Array)
327
+ @vmin =data[0][data_description["values"][0]]
328
+ @vmax =data[0][data_description["values"][0]]
329
+ else
330
+ @vmin =data[0][data_description["values"][0]]
331
+ @vmax =data[0][data_description["values"]]
332
+ end
333
+
334
+ else
335
+ @vmin = 2147483647
336
+ @vmax = -2147483647
337
+ end
338
+ # /* Compute Min and Max values */
339
+ if(scale_mode == SCALE_NORMAL || scale_mode == SCALE_START0)
340
+ @vmin = 0 if (scale_mode == SCALE_START0 )
341
+
342
+ data.each do |key|
343
+ data_description["values"].each do |col_name|
344
+ if(!key[col_name].nil?)
345
+ value = key[col_name]
346
+ if (value.is_a?(Numeric))
347
+ @vmax = value if ( @vmax < value)
348
+ @vmin = value if ( @vmin > value)
349
+ end
350
+ end
351
+ end
352
+ end
353
+ elsif ( scale_mode == SCALE_ADDALL || scale_mode == SCALE_ADDALLSTART0 ) # Experimental
354
+ @vmin = 0 if (scale_mode == SCALE_ADDALLSTART0)
355
+ data.each do |key|
356
+ sum = 0
357
+ data_description["values"].each do|col_name|
358
+ if (!key[col_name].nil?)
359
+ value =key[col_name]
360
+ sum += value if ((value).is_a?(Numeric))
361
+ end
362
+ end
363
+ @vmax = sum if (@vmax < sum)
364
+ @vmin = sum if (@vmin > sum)
365
+ end
366
+
367
+ end
368
+
369
+ if(@vmax.is_a?(String))
370
+ @vmax = @vmax.gsub(/\.[0-9]+/,'')+1 if (@vmax > @vmax.gsub(/\.[0-9]+/,'') )
371
+ end
372
+ # If all values are the same */
373
+ if ( @vmax == @vmin )
374
+ if ( @vmax >= 0 )
375
+ @vmax = @vmax+1
376
+ else
377
+ @vmin = @vmin-1
378
+ end
379
+ end
380
+
381
+ data_range = @vmax - @vmin
382
+ data_range = 0.1 if (data_range == 0 )
383
+
384
+ #Compute automatic scaling */
385
+ scale_ok = false
386
+ factor = 1
387
+ min_div_height = 25
388
+ max_divs = (@g_area_y2 - @g_area_y1)*1.0 / min_div_height
389
+
390
+ if (@vmin == 0 && @vmax == 0 )
391
+ @vmin = 0
392
+ @vmax = 2
393
+ scale = 1
394
+ divisions = 2
395
+ elsif (max_divs > 1)
396
+ while(!scale_ok)
397
+ scale1 = ( @vmax - @vmin )*1.0 / factor
398
+ scale2 = ( @vmax - @vmin )*1.0 /factor / 2
399
+ scale4 = ( @vmax - @vmin )*1.0 / factor / 4
400
+ if ( scale1 > 1 && scale1 <= max_divs && !scale_ok)
401
+ scale_ok = true
402
+ divisions = (scale1).floor
403
+ scale = 1
404
+ end
405
+ if (scale2 > 1 && scale2 <= max_divs && !scale_ok)
406
+ scale_ok = true
407
+ divisions = (scale2).floor
408
+ scale = 2
409
+ end
410
+ if (!scale_ok)
411
+ factor = factor * 10 if ( scale2 > 1 )
412
+ factor = factor / 10 if ( scale2 < 1 )
413
+ end
414
+ end # while end
415
+ if ((((@vmax*1.0 / scale) / factor)).floor != ((@vmax*1.0 / scale) / factor))
416
+ grid_id = ( @vmax*1.0 / scale / factor).floor + 1
417
+ @vmax = grid_id * scale * factor
418
+ divisions = divisions+1
419
+ end
420
+
421
+ if (((@vmin*1.0 / scale) / factor).floor != ((@vmin*1.0 / scale) / factor))
422
+
423
+ grid_id = ( @vmin*1.0 / scale / factor).floor
424
+ @vmin = grid_id * scale * factor*1.0
425
+ divisions = divisions+1
426
+ end
427
+
428
+ else #/* Can occurs for small graphs */
429
+ scale = 1
430
+ end
431
+ divisions = 2 if ( divisions.nil? )
432
+
433
+ divisions = divisions-1 if (scale == 1 && divisions%2 == 1)
434
+
435
+ else
436
+ divisions = @divisions
437
+ end
438
+
439
+ @division_count = divisions
440
+ data_range = @vmax - @vmin
441
+ data_range = 0.1 if (data_range == 0 )
442
+ @division_height = ( @g_area_y2 - @g_area_y1 )*1.0 / divisions
443
+ @division_ratio = ( @g_area_y2 - @g_area_y1 )*1.0 /data_range
444
+ @g_area_x_offset = 0
445
+ if ( data.count > 1 )
446
+ if ( with_margin == false)
447
+ @division_width = ( @g_area_x2 - @g_area_x1 )*1.0 / ((data).count-1)
448
+ else
449
+ @division_width = ( @g_area_x2 - @g_area_x1 ) *1.0/ (data).count
450
+ @g_area_x_offset = @division_width*1.0 / 2
451
+ end
452
+ else
453
+ @division_width = (@g_area_x2 - @g_area_x1)*1.0
454
+ @g_area_x_offset = @division_width*1.0 / 2
455
+ end
456
+
457
+ @data_count = (data).count
458
+ return(0) if (draw_ticks == false )
459
+ ypos = @g_area_y2
460
+ xmin = nil
461
+ i =1
462
+
463
+ while(i<= divisions+1)
464
+ if (right_scale )
465
+ self.draw_line(@g_area_x2,ypos,@g_area_x2+5,ypos,r,g,b)
466
+ else
467
+ self.draw_line(@g_area_x1,ypos,@g_area_x1-5,ypos,r,g,b)
468
+ end
469
+ value = @vmin*1.0 + (i-1) * (( @vmax - @vmin ) / divisions)
470
+ value = (round_of(value * (10**decimals),2)) / (10**decimals)
471
+ value= value.round if value.floor == value.ceil
472
+ value = "#{value} #{data_description['unit']['y']}" if ( data_description["format"]["y"]== "number")
473
+ value = self.to_time(value) if ( data_description["format"]["y"] == "time" )
474
+ value = self.to_date(value) if ( data_description["format"]["y"] == "date" )
475
+ value = self.to_metric(value) if ( data_description["format"]["Y"] == "metric" )
476
+ value = self.to_currency(value) if ( data_description["format"]["Y"] == "currency" )
477
+ position = image_ftb_box(@font_size,0,@font_name,value)
478
+ text_width =position[2]-position[0]
479
+ if ( right_scale )
480
+ image_ttf_text(@picture,@font_size,0,@g_area_x2+10,ypos+(@font_size/2),c_text_color,@font_name,value)
481
+ xmin = @g_area_x2+15+text_width if (xmin.nil? || xmin < @g_area_x2+15+text_width )
482
+ else
483
+ image_ttf_text(@picture,@font_size,0,@g_area_x1-10-text_width,ypos+(@font_size/2),c_text_color,@font_name,value)
484
+ xmin = @g_area_x1-10-text_width if ( xmin.nil? || xmin > @g_area_x1-10-text_width)
485
+ end
486
+ ypos = ypos - @division_height
487
+ i = i+1
488
+ end
489
+ # Write the Y Axis caption if set */
490
+
491
+ if (!data_description["axis"].nil? && !data_description["axis"]["y"].nil? )
492
+ position = image_ftb_box(@font_size,90,@font_name,data_description["axis"]["y"])
493
+ text_height = (position[1]).abs+(position[3]).abs
494
+ text_top = ((@g_area_y2 - @g_area_y1) / 2) + @g_area_y1 + (text_height/2)
495
+
496
+ if (right_scale )
497
+ image_ttf_text(@picture,@font_size,90,xmin+@font_size,text_top,c_text_color,@font_name,data_description["axis"]["y"])
498
+ else
499
+ image_ttf_text(@picture,@font_size,90,xmin-@font_size,text_top,c_text_color,@font_name,data_description["axis"]["y"])
500
+ end
501
+ end
502
+ # Horizontal Axis */
503
+ xpos = @g_area_x1 + @g_area_x_offset
504
+ id = 1
505
+ ymax = nil
506
+ data.each do |key|
507
+ if ( id % skip_labels == 0 )
508
+ self.draw_line((xpos).floor,@g_area_y2,(xpos).floor,@g_area_y2+5,r,g,b)
509
+ value =key[data_description["position"]]
510
+ value = "#{value} #{data_description['unit']['x']}" if ( data_description["format"]["x"] == "number" )
511
+ value = self.to_time(value) if ( data_description["format"]["x"] == "time" )
512
+ value = self.to_date(value) if ( data_description["format"]["x"] == "date" )
513
+ value = self.to_metric(value) if ( data_description["format"]["x"] == "metric" )
514
+ value = self.to_currency(value) if ( data_description["format"]["x"] == "currency" )
515
+ position = image_ftb_box(@font_size,angle,@font_name,value.to_s)
516
+ text_width = (position[2]).abs+(position[0]).abs
517
+ text_height = (position[1]).abs+(position[3]).abs
518
+ if ( angle == 0 )
519
+ ypos = @g_area_y2+18
520
+ image_ttf_text(@picture,@font_size,angle,(xpos).floor-(text_width/2).floor,ypos,c_text_color,@font_name,value.to_s)
521
+ else
522
+ ypos = @g_area_y2+10+text_height
523
+ if ( angle <= 90 )
524
+ image_ttf_text(@picture,@font_size,angle,(xpos).floor-text_width+5,ypos,c_text_color,@font_name,value.to_s)
525
+ else
526
+ image_ttf_text(@picture,@font_size,angle,(xpos).floor+text_width+5,ypos,c_text_color,@font_name,value.to_s)
527
+ end
528
+ end
529
+ ymax = ypos if (ymax.nil? ||(!ymax.nil? && ymax < ypos))
530
+ end
531
+ xpos = xpos + @division_width
532
+ id = id+1
533
+ end #loop ended
534
+ #Write the X Axis caption if set */
535
+
536
+ if ((!data_description["axis"].nil? && !data_description["axis"]["x"].nil?) )
537
+ position = image_ftb_box(@font_size,90,@font_name,data_description["axis"]["x"])
538
+ text_width = (position[2]).abs+(position[0]).abs
539
+ text_left = ((@g_area_x2 - @g_area_x1) / 2) + @g_area_x1 + (text_width/2)
540
+ image_ttf_text(@picture,@font_size,0,text_left,ymax+@font_size+5,c_text_color,@font_name,data_description["axis"]["x"].to_s)
541
+ end
542
+
543
+ end
544
+
545
+ # This function is used by scatter charts.
546
+ # It will compute everything needed to draw the associated line and plot charts.
547
+ # You must specify the name of the two series that will be used as X and Y data. By default this function will compute the min & max values of both series, anyway you can override the automatic scaling by calling first the setFixedScale function.
548
+ def draw_xy_scale(data,data_description,y_serie_name,x_serie_name,r,g,b,with_margin=0,angle=0,decimals=1)
549
+
550
+ self.validate_data("draw_xy_scale",data)
551
+ c_text_color = allocate_color(@picture,r,g,b)
552
+ self.draw_line(@g_area_x1,@g_area_y1,@g_area_x1,@g_area_y2,r,g,b)
553
+ self.draw_line(@g_area_x1,@g_area_y2,@g_area_x2,@g_area_y2,r,g,b)
554
+
555
+ # Process Y scale */
556
+ if(@vmin.nil? && @vmax.nil?)
557
+ @vmin = data[0][y_serie_name]
558
+ @vmax = data[0][y_serie_name]
559
+ data.each do |key|
560
+ if !key[y_serie_name].nil?
561
+ value = key[y_serie_name]
562
+ if (value.is_a?(Numeric))
563
+ @vmax = value if ( @vmax < value)
564
+ @vmin = value if ( @vmin > value)
565
+ end
566
+ end
567
+ end
568
+
569
+ if(@vmax.is_a?(String))
570
+ @vmax = @vmax.gsub(/\.[0-9]+/,'')+1 if (@vmax > @vmax.gsub(/\.[0-9]+/,'') )
571
+ end
572
+ data_range = @vmax - @vmin
573
+ data_range = 0.1 if (data_range == 0 )
574
+
575
+ #Compute automatic scaling
576
+ scale_ok = false
577
+ factor = 1
578
+ min_div_height = 25
579
+ max_divs = (@g_area_y2 - @g_area_y1)*1.0 / min_div_height
580
+ if (@vmin == 0 && @vmax == 0 )
581
+ @vmin = 0
582
+ @vmax = 2
583
+ scale = 1
584
+ divisions = 2
585
+ elsif (max_divs > 1)
586
+ while(!scale_ok)
587
+ scale1 = ( @vmax - @vmin )*1.0 / factor
588
+ scale2 = ( @vmax - @vmin )*1.0 /factor / 2
589
+ scale4 = ( @vmax - @vmin )*1.0 / factor / 4
590
+
591
+ if ( scale1 > 1 && scale1 <= max_divs && !scale_ok)
592
+ scale_ok = true
593
+ divisions = (scale1).floor
594
+ scale = 1
595
+ end
596
+ if ( scale2 > 1 && scale2 <= max_divs && !scale_ok)
597
+ scale_ok = true
598
+ divisions = (scale2).floor
599
+ scale = 2
600
+ end
601
+ if (!scale_ok)
602
+ factor = factor * 10 if ( scale2 > 1 )
603
+ factor = factor / 10 if ( scale2 < 1 )
604
+ end
605
+ end
606
+ if ((((@vmax*1.0 / scale) / factor)).floor != ((@vmax*1.0 / scale) / factor))
607
+ grid_id = ( @vmax*1.0 / scale / factor).floor + 1
608
+ @vmax = grid_id * scale * factor
609
+ divisions = divisions+1
610
+ end
611
+
612
+ if (((@vmin*1.0 / scale) / factor).floor != ((@vmin*1.0 / scale) / factor))
613
+
614
+ grid_id = ( @vmin*1.0 / scale / factor).floor
615
+ @vmin = grid_id * scale * factor*1.0
616
+ divisions = divisions+1
617
+ end
618
+
619
+ else #/* Can occurs for small graphs */
620
+ scale = 1
621
+ end
622
+ divisions = 2 if ( divisions.nil? )
623
+
624
+ if ( is_real_int((@vmax-@vmin)/(divisions-1)))
625
+ divisions-=1
626
+ elsif ( is_real_int((@vmax-@vmin)/(divisions+1)))
627
+ divisions+=1
628
+ end
629
+ else
630
+ divisions =@divisions
631
+ end
632
+ @division_count = divisions
633
+
634
+ data_range = @vmax - @vmin
635
+ data_range = 0.1 if (data_range == 0 )
636
+ @division_height = ( @g_area_y2 - @g_area_y1 )*1.0 / divisions
637
+ @division_ratio = ( @g_area_y2 - @g_area_y1 )*1.0 /data_range
638
+ ypos = @g_area_y2
639
+ xmin = nil
640
+ i =1
641
+
642
+ while(i<= divisions+1)
643
+ self.draw_line(@g_area_x1,ypos,@g_area_x1-5,ypos,r,g,b)
644
+ value = @vmin*1.0 + (i-1) * (( @vmax - @vmin ) / divisions)
645
+ value = (round_of(value * (10**decimals),2)) / (10**decimals)
646
+ value= value.round if value.floor == value.ceil
647
+ value = "#{value} #{data_description['unit']['y']}" if ( data_description["format"]["y"]== "number")
648
+ value = self.to_time(value) if ( data_description["format"]["y"] == "time" )
649
+ value = self.to_date(value) if ( data_description["format"]["y"] == "date" )
650
+ value = self.to_metric(value) if ( data_description["format"]["Y"] == "metric" )
651
+ value = self.to_currency(value) if ( data_description["format"]["Y"] == "currency" )
652
+
653
+ position = image_ftb_box(@font_size,0,@font_name,value)
654
+ text_width =position[2]-position[0]
655
+ image_ttf_text(@picture,@font_size,0,@g_area_x1-10-text_width,ypos+(@font_size/2),c_text_color,@font_name,value)
656
+ xmin = @g_area_x1-10-text_width if ( xmin.nil? || xmin > @g_area_x1-10-text_width)
657
+ ypos = ypos - @division_height
658
+ i = i+1
659
+
660
+ end
661
+
662
+ # Process X scale */
663
+ if(@v_x_min.nil? && @v_x_max.nil?)
664
+
665
+ @v_x_min =data[0][x_serie_name]
666
+ @v_x_max =data[0][x_serie_name]
667
+ data.each do |key|
668
+
669
+ if !key[x_serie_name].nil?
670
+ value = key[x_serie_name]
671
+ if (value.is_a?(Numeric))
672
+
673
+ @v_x_max = value if ( @v_x_max < value)
674
+ @v_x_min = value if ( @v_x_min > value)
675
+ end
676
+ end
677
+ end
678
+
679
+ if (@v_x_max.is_a?(String))
680
+ @v_x_max = @v_x_max.gsub(/\.[0-9]+/,'')+1 if (@v_x_max > @v_x_max.gsub(/\.[0-9]+/,'') )
681
+ end
682
+
683
+ data_range = @vmax - @vmin
684
+ data_range = 0.1 if (data_range == 0 )
685
+
686
+ # Compute automatic scaling
687
+ scale_ok = false
688
+ factor = 1
689
+ min_div_width = 25
690
+ max_divs = (@g_area_x2 - @g_area_x1) / min_div_width
691
+
692
+ if ( @v_x_min == 0 && @v_x_max == 0 )
693
+ @v_x_min = 0
694
+ @v_x_max = 2
695
+ scale = 1
696
+ x_divisions = 2
697
+ elsif (max_divs > 1)
698
+
699
+ while(!scale_ok)
700
+ scale1 = ( @v_x_max - @v_x_min ) / factor
701
+ scale2 = ( @v_x_max - @v_x_min ) / factor / 2
702
+ scale4 = ( @v_x_max - @v_x_min ) / factor / 4
703
+ if ( scale1 > 1 && scale1 <= max_divs && !scale_ok)
704
+ scale_ok = true
705
+ x_divisions = (scale1).floor
706
+ scale = 1
707
+ end
708
+
709
+ if ( scale2 > 1 && scale2 <= max_divs && !scale_ok)
710
+ scale_ok = true
711
+ x_divisions = (scale2).floor
712
+
713
+ scale = 2
714
+ end
715
+ if (!scale_ok)
716
+ factor = factor * 10 if ( scale2 > 1 )
717
+ factor = factor / 10 if ( scale2 < 1 )
718
+ end
719
+ end
720
+
721
+ if ( (@v_x_max*1.0 / scale / factor).floor != @v_x_max / scale / factor)
722
+ grid_id = ( @v_x_max*1.0 / scale / factor).floor + 1
723
+ @v_x_max = grid_id * scale * factor;
724
+ x_divisions+=1
725
+ end
726
+
727
+ if ( (@v_x_min*1.0 / scale / factor).floor != @v_x_min / scale / factor)
728
+ grid_id = floor( @v_x_min / scale / factor);
729
+ @v_x_min = grid_id * scale * factor
730
+ x_divisions+=1
731
+ end
732
+ else #/* Can occurs for small graphs */
733
+ scale = 1;
734
+ end
735
+ x_divisions = 2 if ( x_divisions.nil? )
736
+
737
+ if ( is_real_int((@v_x_max-@v_x_min)/(x_divisions-1)))
738
+ x_divisions-=1
739
+ elsif ( is_real_int((@v_x_max-@v_x_min)/(x_divisions+1)))
740
+ x_divisions+=1
741
+ end
742
+ else
743
+
744
+ x_divisions = @x_divisions
745
+ end
746
+
747
+ @x_division_count = divisions
748
+ @data_count = divisions + 2
749
+
750
+ x_data_range = @v_x_max - @v_x_min
751
+ x_data_range = 0.1 if ( x_data_range == 0 )
752
+
753
+ @division_width = ( @g_area_x2 - @g_area_x1 ) / x_divisions
754
+ @x_division_ratio = ( @g_area_x2 - @g_area_x1 ) / x_data_range
755
+ xpos = @g_area_x1
756
+ ymax =nil
757
+ i=1
758
+
759
+ while(i<= x_divisions+1)
760
+ self.draw_line(xpos,@g_area_y2,xpos,@g_area_y2+5,r,g,b)
761
+ value = @v_x_min + (i-1) * (( @v_x_max - @v_x_min ) / x_divisions)
762
+ value = (round_of(value * (10**decimals),2)) / (10**decimals)
763
+ value= value.round if value.floor == value.ceil
764
+ value = "#{value}#{data_description['unit']['y']}" if ( data_description["format"]["y"]== "number")
765
+ value = self.to_time(value) if ( data_description["format"]["y"] == "time" )
766
+ value = self.to_date(value) if ( data_description["format"]["y"] == "date" )
767
+ value = self.to_metric(value) if ( data_description["format"]["Y"] == "metric" )
768
+ value = self.to_currency(value) if ( data_description["format"]["Y"] == "currency" )
769
+ position = image_ftb_box(@font_size,angle,@font_name,value)
770
+ text_width =position[2].abs+position[0].abs
771
+ text_height = position[1].abs+position[3].abs
772
+
773
+ if ( angle == 0 )
774
+ ypos = @g_area_y2+18
775
+ image_ttf_text(@picture,@font_size,angle,(xpos).floor-(text_width/2).floor,ypos,c_text_color,@font_name,value)
776
+ else
777
+
778
+ ypos = @g_area_y2+10+text_height
779
+ if ( angle <= 90 )
780
+ image_ttf_text(@picture,@font_size,angle,(xpos).floor-text_width+5,ypos,c_text_color,@font_name,value)
781
+ else
782
+ image_ttf_text(@picture,@font_size,angle,(xpos).floor+text_width+5,ypos,c_text_color,@font_name,value)
783
+ end
784
+
785
+ end
786
+
787
+ ymax = ypos if (ymax.nil? || ymax < ypos)
788
+ i=i+1
789
+ xpos = xpos + @division_width
790
+ end
791
+ #Write the Y Axis caption if set
792
+ if ((!data_description["axis"].nil? && !data_description["axis"]["y"].nil?) )
793
+ position = image_ftb_box(@font_size,90,@font_name,data_description["axis"]["y"])
794
+ text_height = (position[1]).abs+(position[3]).abs
795
+ text_top = ((@g_area_y2 - @g_area_y1) / 2) + @g_area_y1 + (text_width/2)
796
+ image_ttf_text(@picture,@font_size,90,xmin-@font_size,text_top,c_text_color,@font_name,data_description["axis"]["y"].to_s)
797
+ end
798
+ if ((!data_description["axis"].nil? && !data_description["axis"]["x"].nil?) )
799
+ position = image_ftb_box(@font_size,90,@font_name,data_description["axis"]["x"])
800
+ text_width = (position[2]).abs+(position[0]).abs
801
+ text_left = ((@g_area_x2 - @g_area_x1) / 2) + @g_area_x1 + (text_width/2)
802
+ image_ttf_text(@picture,@font_size,0,text_left,ymax+@font_size+5,c_text_color,@font_name,data_description["axis"]["x"].to_s)
803
+ end
804
+
805
+ end
806
+
807
+ # This function will draw a grid over the graph area.
808
+ # line_width will be passed to the draw_dotted_line function.
809
+ # The r,g,b 3 parameters are used to set the grid color.
810
+ # Setting mosaic to true will draw grey area between two lines.
811
+ # You can define the transparency factor of the mosaic area playing with the alpha parameter.
812
+
813
+ def draw_grid(line_width,mosaic=true,r=220,g=220,b=220,alpha=100)
814
+ # Draw mosaic */
815
+ if (mosaic)
816
+ layer_width = @g_area_x2-@g_area_x1
817
+ layer_height = @g_area_y2-@g_area_y1
818
+
819
+ @layers[0] = image_create_true_color(layer_width,layer_height)
820
+ #c_white = allocate_color(@layers[0],255,255,255);
821
+ image_filled_rectangle(@layers[0],0,0,layer_width,layer_height,255,255,255)
822
+ image_color_transparent(@layers[0],255,255,255)
823
+
824
+ #c_rectangle =allocate_color(@layers[0],250,250,250);
825
+
826
+ y_pos = layer_height #@g_area_y2-1
827
+ last_y = y_pos
828
+ i =0
829
+ while(i<=@division_count)
830
+ last_y= y_pos
831
+ y_pos = y_pos - @division_height
832
+ y_pos = 1 if ( y_pos <= 0 )
833
+ image_filled_rectangle(@layers[0],1, y_pos,layer_width-1,last_y,250,250,250) if ( i % 2 == 0 )
834
+ i = i+1
835
+ end
836
+ image_copy_merge(@layers[0],@picture,@g_area_x1,@g_area_y1,0,0,layer_width,layer_height,alpha);
837
+ @layers[0].destroy
838
+ end
839
+
840
+ #Horizontal lines
841
+ y_pos = @g_area_y2 - @division_height
842
+ i=1
843
+ while(i<=@division_count)
844
+ self.draw_dotted_line(@g_area_x1,y_pos,@g_area_x2,y_pos,line_width,r,g,b) if ( y_pos > @g_area_y1 && y_pos < @g_area_y2 )
845
+ y_pos = y_pos - @division_height
846
+ i = i+1
847
+ end
848
+ # Vertical lines
849
+ if (@g_area_x_offset == 0 )
850
+ x_pos = @g_area_x1 + (@division_width) +@g_area_x_offset
851
+ col_count = (@data_count.to_f-2).floor
852
+ else
853
+
854
+ x_pos = @g_area_x1 +@g_area_x_offset
855
+ col_count = ( (@g_area_x2 - @g_area_x1) / @division_width )
856
+ end
857
+ i= 1
858
+
859
+ while (i<=col_count)
860
+ if ( x_pos > @g_area_x1 && x_pos < @g_area_x2 )
861
+ self.draw_dotted_line((x_pos).floor,@g_area_y1,(x_pos).floor,@g_area_y2,line_width,r,g,b)
862
+ end
863
+ x_pos = x_pos + @division_width
864
+ i= i+1
865
+ end
866
+ end
867
+
868
+
869
+ # This function evaluate the width and height of the box generated by the draw_legend.
870
+ # This will help you to calculate dynamicaly the position where you want to print it (eg top-right).
871
+ # You must provide the data_description array as only parameter.
872
+ # This function will return and array containing in the first row the width of the box and in the second row the height of the box.
873
+
874
+ def get_legend_box_size(data_description)
875
+ return(-1) if data_description["description"].nil?
876
+ # <-10->[8]<-4->Text<-10->
877
+ max_width = 0
878
+ max_height = 8
879
+ data_description["description"].each do |key,value|
880
+ position = image_ftb_box(@font_size,0,@font_name,value)
881
+ text_width = position[2]-position[0]
882
+ text_height = position[1]-position[7]
883
+ max_width = text_width if (text_width > max_width)
884
+ max_height = max_height + text_height + 4
885
+ end
886
+ max_height = max_height - 3
887
+ max_width = max_width + 32
888
+
889
+ [max_width,max_height]
890
+ end
891
+ # This function will draw the legend of the graph ( serie color & serie name ) at the specified position.
892
+ # The r,g,bparameters are used to set the background color. You can optionally provide the shadow color using the rs,gs,bs parameters.
893
+ # You can also customize the text color using the rt,gt,bt.
894
+ # Setting Border to false remove the surrounding box.
895
+
896
+ def draw_legend(x_pos,y_pos,data_description,r,g,b,rs=-1,gs=-1,bs=-1,rt=0,gt=0,bt=0,border=true)
897
+ #Validate the Data and data_description array
898
+ data_description = self.validate_data_description("draw_legend",data_description)
899
+ return(-1) if (data_description["description"].nil?)
900
+ c_text_color = allocate_color(@picture, rt, gt, bt)
901
+ # <-10->[8]<-4->Text<-10->
902
+ max_width = 0
903
+ max_height = 8
904
+ data_description["description"].each do |key,value|
905
+ position = image_ftb_box(@font_size,0,@font_name,value)
906
+ text_width = position[2]-position[0]
907
+ text_height = position[1]-position[7]
908
+ max_width = text_width if ( text_width > max_width)
909
+ max_height = max_height + text_height + 4
910
+ end
911
+ max_height = max_height - 5
912
+ max_width = max_width + 32
913
+ if ( rs == -1 || gs == -1 || bs == -1 )
914
+ rs = r-30
915
+ gs = g-30
916
+ bs = b-30
917
+ end
918
+ if ( border )
919
+ self.draw_filled_rounded_rectangle(x_pos+1,y_pos+1,x_pos+max_width+1,y_pos+max_height+1,5,rs,gs,bs)
920
+ self.draw_filled_rounded_rectangle(x_pos,y_pos,x_pos+max_width,y_pos+max_height,5,r,g,b)
921
+ end
922
+ y_offset = 4 + @font_size
923
+ id = 0
924
+ data_description["description"].each do |key,value|
925
+ self.draw_filled_rounded_rectangle(x_pos+10,y_pos+y_offset-4 , x_pos+14, y_pos+y_offset-4, 2, @palette[id]["r"], @palette[id]["g"], @palette[id]["b"])
926
+ image_ttf_text(@picture, @font_size,0, x_pos+22, y_pos+y_offset, c_text_color, @font_name, value)
927
+ position = image_ftb_box(@font_size,0,@font_name,value);
928
+ text_height = position[1]-position[7]
929
+ y_offset = y_offset + text_height + 4
930
+ id=id+1
931
+ end
932
+ end
933
+
934
+ # This function will draw the legend of a pie graph ( serie color & value name ).
935
+ # Be carrefull, dataset used for pie chart are not the same than for other line / curve / plot graphs.
936
+ # You can specify the position of the legend box and the background color.
937
+ def draw_pie_legend(x_pos,y_pos,data,data_description,r,g,b)
938
+ data_description = self.validate_data_description("draw_pie_legend",data_description,false)
939
+ self.validate_data("draw_pie_legend",data)
940
+ return(-1) if (data_description["position"].nil?)
941
+ c_text_color = allocate_color(@picture,0,0,0)
942
+
943
+ # <-10->[8]<-4->Text<-10-> */
944
+ max_width = 0
945
+ max_height = 8
946
+ data.each do |key|
947
+ value = key[data_description["position"]]
948
+ position = image_ftb_box(@font_size,0,@font_name,value)
949
+ text_width = position[2]-position[0]
950
+ text_height = position[1]-position[7]
951
+ max_width = text_width if ( text_width > max_width)
952
+ max_height = max_height + text_height + 4
953
+ end
954
+ max_height = max_height - 3
955
+ max_width = max_width + 32
956
+ self.draw_filled_rounded_rectangle(x_pos+1,y_pos+1,x_pos+max_width+1,y_pos+max_height+1,5,r-30,g-30,b-30)
957
+ self.draw_filled_rounded_rectangle(x_pos,y_pos,x_pos+max_width,y_pos+max_height,5,r,g,b)
958
+ y_offset = 4 + @font_size
959
+ id = 0
960
+ data.each do |key|
961
+ value = key[data_description["position"]]
962
+ position = image_ftb_box(@font_size,0,@font_name,value);
963
+ text_height = position[1]-position[7]
964
+ self.draw_filled_rectangle(x_pos+10,y_pos+y_offset-6,x_pos+14,y_pos+y_offset-2,@palette[id]["r"],@palette[id]["g"],@palette[id]["b"]);
965
+ image_ttf_text(@picture,@font_size,0,x_pos+22,y_pos+y_offset,c_text_color,@font_name,value)
966
+ y_offset = y_offset + text_height + 4
967
+ id= id+1
968
+ end
969
+ end
970
+
971
+ # This function is used to write the graph title.
972
+ # Used with default parameters you must specify the bottom left position of the text.
973
+ # if you are specifying x2 and y2 the text will be centered horizontaly and verticaly in the box of coordinates (x1,y1)-(x2,y2).
974
+ # value correspond to the text that will be written on the graph.
975
+ # r, g and b are used to set the text color.
976
+ # Setting shadow to true will makes a shadow behind the text.
977
+ def draw_title(x_pos,y_pos,value,r,g,b,x_pos2=-1,y_pos2=-1,shadow=false)
978
+ c_text_color = allocate_color(@picture, r, g, b)
979
+ if ( x_pos2 != -1 )
980
+ position = image_ftb_box(@font_size,0,@font_name,value)
981
+ text_width = position[2]-position[0]
982
+ x_pos =(( x_pos2 - x_pos -text_width ) / 2 ).floor + x_pos
983
+ end
984
+ if ( y_pos2 != -1 )
985
+ position = image_ftb_box(@font_size,0,@font_name,value)
986
+ text_height = position[5]-position[3]
987
+ y_pos =(( y_pos2 - y_pos - text_height ) / 2 ).floor + y_pos
988
+ end
989
+ if ( shadow )
990
+ c_shadow_color = allocate_color(@picture,@shadow_r_color,@shadow_g_color,@shadow_b_color)
991
+ image_ttf_text(@picture,@font_size,0,x_pos+@shadow_x_distance,y_pos+@shadow_y_distance, c_shadow_color ,@font_name,value)
992
+ end
993
+ image_ttf_text(@picture,@font_size,0,x_pos,y_pos,c_text_color,@font_name,value);
994
+ end
995
+
996
+ # Use this function to write text over the picture.
997
+ # You must specify the coordinate of the box where the text will be written using the (x1,y1)-(x2,y2) parameters, the text angle and the text color with the r,g,b parameters.
998
+ # You can choose how the text will be aligned with the align parameter :
999
+ # * Rchart:: ALIGN_TOP_LEFT Use the box top left corner.
1000
+ # * Rchart:: ALIGN_TOP_CENTER Use the box top center corner.
1001
+ # * Rchart:: ALIGN_TOP_RIGHT Use the box top right corner.
1002
+ # * Rchart:: ALIGN_LEFT Use the center left.
1003
+ # * Rchart:: ALIGN_CENTER Use the center.
1004
+ # * Rchart:: ALIGN_RIGHT Use the center right.
1005
+ # * Rchart:: ALIGN_BOTTOM_LEFT Use the box bottom left corner.
1006
+ # * Rchart:: ALIGN_BOTTOM_CENTER Use the box bottom center corner.
1007
+ # * Rchart:: ALIGN_BOTTOM_RIGHT Use the box bottom right corner.
1008
+
1009
+ def draw_text_box(x1,y1,x2,y2,text,angle=0,r=255,g=255,b=255,align=ALIGN_LEFT,shadow=true,bgr=-1,bgg=-1,bgb=-1,alpha=100)
1010
+ position = image_ftb_box(@font_size,angle,@font_name,text)
1011
+ text_width = position[2]-position[0]
1012
+ text_height = position[5]-position[3]
1013
+ area_width = x2 - x1
1014
+ area_height = y2 - y1
1015
+ x =nil
1016
+ y = nil
1017
+
1018
+ if ( bgr != -1 && bgg != -1 && bgb != -1 )
1019
+ self.draw_filled_rectangle(x1,y1,x2,y2,bgr,bgg,bgb,false,alpha)
1020
+ end
1021
+
1022
+ if ( align == ALIGN_TOP_LEFT )
1023
+ x = x1+1
1024
+ y = y1+@font_size+1
1025
+ end
1026
+
1027
+ if ( align == ALIGN_TOP_CENTER )
1028
+ x = x1+(area_width/2)-(text_width/2)
1029
+ y = y1+@font_size+1
1030
+ end
1031
+
1032
+ if ( align == ALIGN_TOP_RIGHT )
1033
+ x = x2-text_width-1
1034
+ y = y1+@font_size+1
1035
+ end
1036
+ if ( align == ALIGN_LEFT )
1037
+ x = x1+1
1038
+ y = y1+(area_height/2)-(text_height/2)
1039
+ end
1040
+ if ( align == ALIGN_CENTER )
1041
+ x = x1+(area_width/2)-(text_width/2)
1042
+ y = y1+(area_height/2)-(text_height/2)
1043
+ end
1044
+ if ( align == ALIGN_RIGHT )
1045
+ x = x2-text_width-1
1046
+ y = y1+(area_height/2)-(text_height/2)
1047
+ end
1048
+ if ( align == ALIGN_BOTTOM_LEFT )
1049
+ x = x1+1
1050
+ y = y2-1
1051
+ end
1052
+ if ( align == ALIGN_BOTTOM_CENTER )
1053
+ x = x1+(area_width/2)-(text_width/2)
1054
+ y = y2-1
1055
+ end
1056
+ if ( align == ALIGN_BOTTOM_RIGHT )
1057
+ x = x2-text_width-1
1058
+ y = y2-1
1059
+ end
1060
+ c_text_color =allocate_color(@picture,r,g,b)
1061
+ c_shadow_color =allocate_color(@picture,0,0,0)
1062
+ if ( shadow )
1063
+ image_ttf_text(@picture,@font_size,angle,x+1,y+1,c_shadow_color,@font_name,text)
1064
+ end
1065
+
1066
+ image_ttf_text(@picture,@font_size,angle,x,y,c_text_color,@font_name,text)
1067
+ end
1068
+
1069
+ # This function will draw an horizontal treshold ( this is an easy way to draw the 0 line ).
1070
+ # If show_label is set to true, the value of the treshold will be written over the graph.
1071
+ # If show_on_right is set to true, the value will be written on the right side of the graph.
1072
+ # r, g and b are used to set the line and text color.
1073
+ # Use tick_width to set the width of the ticks, if set to 0 this will draw a solid line.
1074
+ # You can optionnaly provide the caption of the treshold (by default the treshold value is used)
1075
+
1076
+ def draw_treshold(value,r,g,b,show_label=false,show_on_right=false,tick_width=4,free_text=nil)
1077
+ b, g, r = validate_color(b, g, r)
1078
+
1079
+ c_text_color =allocate_color(@picture,r,g,b)
1080
+ # c_text_color = GD2::Color.new(r,g,b)
1081
+ y = @g_area_y2 - (value - @vmin.to_f) * @division_ratio.to_f
1082
+
1083
+ return(-1) if ( y <= @g_area_y1 || y >= @g_area_y2 )
1084
+ if ( tick_width == 0 )
1085
+ self.draw_line(@g_area_x1,y,@g_area_x2,y,r,g,b)
1086
+ else
1087
+ self.draw_dotted_line(@g_area_x1,y,@g_area_x2,y,tick_width,r,g,b)
1088
+ end
1089
+ if (show_label )
1090
+ if ( free_text.nil? )
1091
+ label = value
1092
+ else
1093
+ label = free_text
1094
+ end
1095
+
1096
+ if ( show_on_right )
1097
+ image_ttf_text(@picture,@font_size,0,@g_area_x2+2,y+(@font_size/2),c_text_color,@font_name,label.to_s)
1098
+ else
1099
+ image_ttf_text(@picture,@font_size,0,@g_area_x1+2,y-(@font_size/2),c_text_color,@font_name,label.to_s)
1100
+ end
1101
+ end
1102
+ end
1103
+
1104
+ # This function will draw a label over the graph.
1105
+ # You must specify the data & data_description structures, the serie name ( "Serie1" by default if only one ),
1106
+ # the x position of the value in the data array (will be numeric starting at 0 if no abscise_label are defined or the value of the selected abscise serie if specified), the caption that will displayed and optionally the color of the label
1107
+
1108
+ def set_label(data,data_description,serie_name,value_name,caption,r=210,g=210,b=210)
1109
+ data_description = self.validate_data_description("set_label",data_description)
1110
+ self.validate_data("set_label",data)
1111
+ shadow_factor = 100
1112
+ c_label =allocate_color(@picture,r,g,b)
1113
+ c_shadow =allocate_color(@picture,r-shadow_factor,g-shadow_factor,b-shadow_factor)
1114
+ c_text_color =allocate_color(@picture,0,0,0)
1115
+ cp = 0
1116
+ found = false
1117
+ numerical_value = 0
1118
+ data.each do |key|
1119
+ if key[data_description["position"]].to_s == value_name.to_s
1120
+ numerical_value = key[serie_name]
1121
+ found = true
1122
+ end
1123
+ cp +=1 if !found
1124
+ end
1125
+
1126
+ xpos = @g_area_x1 + @g_area_x_offset + ( @division_width * cp ) + 2
1127
+ ypos = @g_area_y2 - (numerical_value - @vmin) *@division_ratio
1128
+ position = image_ftb_box(@font_size,0,@font_name,caption)
1129
+ text_height = position[3] - position[5]
1130
+ text_width = position[2]-position[0] + 2
1131
+ text_offset = (text_height/2).floor
1132
+ # Shadow
1133
+ poly = [xpos+1,ypos+1,xpos + 9,ypos - text_offset,xpos + 8,ypos + text_offset + 2]
1134
+ image_filled_polygon(@picture,poly,r-shadow_factor,g-shadow_factor,b-shadow_factor,3)
1135
+ self.draw_line(xpos,ypos+1,xpos + 9,ypos - text_offset - 0.2,r-shadow_factor,g-shadow_factor,b-shadow_factor)
1136
+ self.draw_line(xpos,ypos+1,xpos + 9,ypos + text_offset + 2.2,r-shadow_factor,g-shadow_factor,b-shadow_factor)
1137
+ self.draw_filled_rectangle(xpos + 9,ypos - text_offset-0.2,xpos + 13 + text_width,ypos + text_offset + 2.2,r-shadow_factor,g-shadow_factor,b-shadow_factor)
1138
+
1139
+ #Label background
1140
+ poly = [xpos,ypos,xpos + 8,ypos - text_offset - 1,xpos + 8,ypos + text_offset + 1]
1141
+ image_filled_polygon(@picture,poly,r,g,b,3)
1142
+ self.draw_line(xpos-1,ypos,xpos + 8,ypos - text_offset - 1.2,r,g,b)
1143
+ self.draw_line(xpos-1,ypos,xpos + 8,ypos + text_offset + 1.2,r,g,b)
1144
+ self.draw_filled_rectangle(xpos + 8,ypos - text_offset - 1.2,xpos + 12 + text_width,ypos + text_offset + 1.2,r,g,b)
1145
+
1146
+ image_ttf_text(@picture,@font_size,0,xpos + 10,ypos + text_offset,c_text_color,@font_name,caption)
1147
+ end
1148
+
1149
+ # This function will draw a plot graph using all the registered series.
1150
+ # Giving only the data & data_description structure will draw the basic plot graph,
1151
+ # You can specify the radius ( external & internal ) of the plots.
1152
+ # You can also specify the color of the points ( will be unique in case of multiple series ).
1153
+ # Setting Shadow to true will draw a shadow under the plots.
1154
+
1155
+ def draw_plot_graph(data,data_description,big_radius=5,small_radius=2,r2=-1,g2=-1,b2=-1,shadow=false)
1156
+ #/* Validate the Data and data_description array */
1157
+ data_description = self.validate_data_description("draw_plot_graph",data_description)
1158
+ self.validate_data("draw_plot_graph",data)
1159
+ graph_id = 0
1160
+ ro = r2
1161
+ go = g2
1162
+ bo = b2
1163
+ id =0
1164
+ color_id =0
1165
+ data_description["values"].each do |col_name|
1166
+ data_description["description"].each do |key_i,value_i|
1167
+ if ( key_i == col_name )
1168
+ color_id = id
1169
+ id = id+1
1170
+ end
1171
+ end
1172
+ r = @palette[color_id]["r"];
1173
+ g = @palette[color_id]["g"];
1174
+ b = @palette[color_id]["b"];
1175
+ r2 = ro
1176
+ g2 = go
1177
+ b2 = bo
1178
+ #TODO convert this function
1179
+
1180
+ if ( !data_description["symbol"].nil? && !data_description["symbol"][col_name].nil?)
1181
+ is_alpha = false # ((ord ( file_get_contents (data_description["symbol"][col_name], false, NULL, 25, 1)) & 6) & 4) == 4;
1182
+ im_symbol = image_create_from_png(data_description["symbol"][col_name])
1183
+ infos = get_image_size(im_symbol)
1184
+ image_width = infos[0]
1185
+ image_height = infos[1]
1186
+ #
1187
+ end
1188
+
1189
+ x_pos = @g_area_x1 + @g_area_x_offset
1190
+ h_size = (big_radius/2).round
1191
+ r3 = -1
1192
+ g3 = -1
1193
+ b3 = -1
1194
+ data.each do |key|
1195
+ value= key[col_name]
1196
+ if value.is_a?(Numeric)
1197
+ y_pos = @g_area_y2 - ((value-@vmin) * @division_ratio)
1198
+ else
1199
+ y_pos = @g_area_y2 - ((0-@vmin) * @division_ratio)
1200
+ end
1201
+
1202
+
1203
+ # Save point into the image map if option activated
1204
+ if ( @build_map )
1205
+ #add_to_image_map(x_pos-h_size,y_pos-h_size,x_pos+1+h_size,y_pos+h_size+1,data_description["description"][col_name],key[col_name].data_description["unit"]["y"],"Plot");
1206
+ end
1207
+
1208
+ if(value.is_a?(Numeric))
1209
+ #MY Hack
1210
+ if (data_description["symbol"].nil? || data_description["symbol"][col_name].nil? )
1211
+ if ( shadow )
1212
+ if ( r3 !=-1 && g3 !=-1 && b3 !=-1 )
1213
+ self.draw_filled_circle(x_pos+2,y_pos+2,big_radius,r3,g3,b3)
1214
+ else
1215
+ r3 = @palette[color_id]["r"]-20
1216
+ r3 = 0 if ( r3 < 0 )
1217
+ g3 = @palette[color_id]["g"]-20
1218
+ g3 = 0 if ( g3 < 0 )
1219
+ b3 = @palette[color_id]["b"]-20
1220
+ b3 = 0 if ( b3 < 0 )
1221
+ self.draw_filled_circle(x_pos+2,y_pos+2,big_radius,r3,g3,b3)
1222
+ end
1223
+ end
1224
+ self.draw_filled_circle(x_pos+1,y_pos+1,big_radius,r,g,b)
1225
+ if ( small_radius != 0 )
1226
+ if ( r2 !=-1 && g2 !=-1 && b2 !=-1 )
1227
+ self.draw_filled_circle(x_pos+1,y_pos+1,small_radius,r2,g2,b2);
1228
+ else
1229
+ r2 = @palette[color_id]["r"]-15
1230
+ r2 = 0 if ( r2 < 0 )
1231
+ g2 = @palette[color_id]["g"]-15
1232
+ g2 = 0 if ( g2 < 0 )
1233
+ b2 = @palette[color_id]["b"]-15
1234
+ b2 = 0 if ( b2 < 0 )
1235
+ self.draw_filled_circle(x_pos+1,y_pos+1,small_radius,r2,g2,b2)
1236
+ end
1237
+ end
1238
+ else
1239
+ image_copy_merge(im_symbol,@picture,x_pos+1-image_width/2,y_pos+1-image_height/2,0,0,image_width,image_height,100)
1240
+ end
1241
+ end
1242
+ x_pos = x_pos + @division_width
1243
+ end
1244
+ graph_id+=1
1245
+ end
1246
+ end
1247
+
1248
+ # This function is very similar as the draw_plot_graph function.
1249
+ # You must specify the name of the two series that will be used as x and y coordinates and the color id to use.
1250
+
1251
+ def draw_xy_plot_graph(data,data_description,y_serie_name,x_serie_name,palette_id=0,big_radius=5,small_radius=2,r2=-1,g2=-1,b2=-1,shadow=true)
1252
+ r = @palette[palette_id]["r"];
1253
+ g = @palette[palette_id]["g"];
1254
+ b = @palette[palette_id]["b"];
1255
+ r3 = -1
1256
+ g3 = -1
1257
+ b3 = -1
1258
+
1259
+ y_last = -1
1260
+ x_last = -1
1261
+ data.each do |key|
1262
+ if (!key[y_serie_name].nil? && !key[x_serie_name])
1263
+ x = key[x_serie_name]
1264
+ y = key[y_serie_name]
1265
+ y = @g_area_y2 - ((y-@vmin) * @division_ratio)
1266
+ x = @g_area_x1 + ((x-@v_x_min) * @x_division_ratio)
1267
+ if ( shadow )
1268
+ if ( r3 !=-1 && g3 !=-1 && b3 !=-1 )
1269
+ self.draw_filled_circle(x+2,y+2,big_radius,r3,g3,b3)
1270
+ else
1271
+ r3 = @palette[palette_id]["r"]-20
1272
+ r = 0 if ( r < 0 )
1273
+ g3 = @palette[palette_id]["g"]-20
1274
+ g = 0 if ( g < 0 )
1275
+ b3 = @palette[palette_id]["b"]-20
1276
+ b = 0 if ( b < 0 )
1277
+ self.draw_filled_circle(x+2,y+2,big_radius,r3,g3,b3)
1278
+ end
1279
+ end
1280
+ self.draw_filled_circle(x+1,y+1,big_radius,r,g,b);
1281
+
1282
+ if ( r2 !=-1 && g2 !=-1 && b2 !=-1 )
1283
+ self.draw_filled_circle(x+1,y+1,small_radius,r2,g2,b2)
1284
+ else
1285
+ r2 = @palette[palette_id]["r"]+20
1286
+ r = 255 if ( r > 255 )
1287
+ g2 = @palette[palette_id]["g"]+20
1288
+ g = 255 if ( g > 255 )
1289
+ b2 = @palette[palette_id]["b"]+20
1290
+ b = 255 if ( b > 255 )
1291
+ self.draw_filled_circle(x+1,y+1,small_radius,r2,g2,b2);
1292
+ end
1293
+ end
1294
+ end
1295
+ end
1296
+
1297
+ # This function will draw an area between two data series.
1298
+ # extracting the minimum and maximum value for each X positions.
1299
+ # You must specify the two series name and the area color.
1300
+ # You can specify the transparency which is set to 50% by default.
1301
+
1302
+ def draw_area(data,serie1,serie2,r,g,b,alpha = 50)
1303
+ self.validate_data("draw_area",data)
1304
+ layer_width = @g_area_x2-@g_area_x1
1305
+ layer_height = @g_area_y2-@g_area_y1
1306
+
1307
+ @layers[0] = image_create_true_color(layer_width,layer_height)
1308
+ image_filled_rectangle(@layers[0],0,0,layer_width,layer_height,255,255,255)
1309
+ image_color_transparent(@layers[0],255,255,255)
1310
+
1311
+ x_pos = @g_area_x_offset
1312
+ last_x_pos = -1
1313
+ last_y_pos1 = nil
1314
+ last_y_pos2= nil
1315
+ data.each do |key|
1316
+ value1 = key[serie1]
1317
+ value2 = key[serie2]
1318
+ y_pos1 = layer_height - ((value1-@vmin) * @division_ratio)
1319
+ y_pos2 = layer_height - ((value2-@vmin) * @division_ratio)
1320
+
1321
+ if ( last_x_pos != -1 )
1322
+ points = []
1323
+ points << last_x_pos
1324
+ points << last_y_pos1
1325
+ points << last_x_pos
1326
+ points << last_y_pos2
1327
+ points << x_pos
1328
+ points << y_pos2
1329
+ points << x_pos
1330
+ points << y_pos1
1331
+ image_filled_polygon(@layers[0],points,r,g,b,4)
1332
+ end
1333
+ last_y_pos1 = y_pos1
1334
+ last_y_pos2 = y_pos2
1335
+ last_x_pos = x_pos
1336
+ x_pos= x_pos+ @division_width
1337
+ end
1338
+ image_copy_merge(@layers[0],@picture,@g_area_x1,@g_area_y1,0,0,layer_width,layer_height,alpha);
1339
+ image_destroy(@layers[0])
1340
+ end
1341
+
1342
+ # You can use this function to display the values contained in the series on top of the charts.
1343
+ # It is possible to specify one or multiple series to display using and array.
1344
+ def write_values(data,data_description,series)
1345
+
1346
+ data_description = self.validate_data_description("write_values",data_description)
1347
+ self.validate_data("write_values",data)
1348
+ series = [series] if ( !series.is_a?(Array))
1349
+ id = 0
1350
+ color_id =0
1351
+ series.each do |col_name|
1352
+ data_description["description"].each do |key_i,value_i|
1353
+ if ( key_i == col_name )
1354
+ color_id = id
1355
+ id = id+1
1356
+ end
1357
+ end
1358
+ xpos = @g_area_x1 + @g_area_x_offset
1359
+ xlast = -1
1360
+ data.each do |key|
1361
+ if ((!key[col_name].nil?) && (key[col_name].is_a?(Numeric)))
1362
+ value = key[col_name]
1363
+ ypos = @g_area_y2 - ((value-@vmin) * @division_ratio)
1364
+ positions = image_ftb_box(@font_size,0,@font_name,value.to_s)
1365
+ width = positions[2] - positions[6]
1366
+ x_offset = xpos - (width/2)
1367
+ height = positions[3] - positions[7]
1368
+ y_offset = ypos - 4
1369
+
1370
+ c_text_color = allocate_color(@picture,@palette[color_id]["r"],@palette[color_id]["g"],@palette[color_id]["b"]);
1371
+ image_ttf_text(@picture,@font_size,0,x_offset,y_offset,c_text_color,@font_name,value.to_s)
1372
+ end
1373
+ xpos = xpos + @division_width
1374
+ end
1375
+ end
1376
+ end
1377
+
1378
+ # This function will draw a line graph using all the registered series.
1379
+ def draw_line_graph(data,data_description,serie_name="")
1380
+ data_description = self.validate_data_description("draw_line_graph",data_description)
1381
+ self.validate_data("draw_line_graph",data)
1382
+ graph_id = 0
1383
+ color_id =0
1384
+ id =0
1385
+ data_description["values"].each do |col_name|
1386
+ data_description["description"].each do |key_i,value_i|
1387
+ if ( key_i == col_name )
1388
+ color_id = id
1389
+ id = id+1
1390
+ end
1391
+ end
1392
+ if ( serie_name == "" || serie_name == col_name )
1393
+ x_pos = @g_area_x1 + @g_area_x_offset
1394
+ x_last = -1
1395
+ y_last = -1
1396
+ data.each do |key|
1397
+ if(!key[col_name].nil?)
1398
+ value = key[col_name]
1399
+ if(value.is_a?(Numeric))
1400
+ y_pos= @g_area_y2 - ((value-@vmin) * @division_ratio)
1401
+ else
1402
+ y_pos= @g_area_y2 - ((0-@vmin) * @division_ratio)
1403
+ end
1404
+ # /* Save point into the image map if option activated */
1405
+ if ( @build_map )
1406
+ #self.add_to_image_map(x_pos-3,y_pos-3,x_pos+3,y_pos+3,data_description["description"][col_name],data[key][col_name].data_description["unit"]["y"],"Line");
1407
+ end
1408
+ x_last = -1 if(!value.is_a?(Numeric))
1409
+ if ( x_last != -1 )
1410
+ self.draw_line(x_last,y_last,x_pos,y_pos,@palette[color_id]["r"],@palette[color_id]["g"],@palette[color_id]["b"],true)
1411
+ end
1412
+ x_last = x_pos
1413
+ y_last = y_pos
1414
+ x_last = -1 if(!value.is_a?(Numeric))
1415
+ end
1416
+ x_pos = x_pos + @division_width
1417
+ end
1418
+ graph_id+=1
1419
+ end
1420
+ end
1421
+ end
1422
+
1423
+ # This function will draw a scatter line graph.
1424
+ # You must specify the x and y series that will be used.
1425
+ # You can optionnaly set the color index in the current palette.
1426
+ def draw_xy_graph(data,data_description,y_serie_name,x_serie_name,palette_id=0)
1427
+ y_last = -1
1428
+ x_last = -1
1429
+ data.each do |key|
1430
+ if ( !key[y_serie_name].nil? && !key[x_serie_name].nil? )
1431
+ x= key[x_serie_name]
1432
+ y = key[y_serie_name]
1433
+ y = @g_area_y2 - ((y-@vmin) * @division_ratio);
1434
+ x= @g_area_x1 + ((x-@v_x_min) * @x_division_ratio);
1435
+ if (x_last != -1 && y_last != -1)
1436
+ self.draw_line(x_last,y_last,x,y,@palette[palette_id]["r"],@palette[palette_id]["g"],@palette[palette_id]["b"],true)
1437
+ end
1438
+ x_last = x
1439
+ y_last = y
1440
+ end
1441
+ end
1442
+ end
1443
+
1444
+ # This function will draw a curved line graph using all the registered series.
1445
+ # This curve is using a cubic algorythm to process the average values between two points.
1446
+ # You have to specify the accuracy between two points, typicaly a 0.1 value is acceptable. the smaller the value is, the longer it will take to process the graph.
1447
+ def draw_cubic_curve(data,data_description,accuracy=0.1,serie_name="")
1448
+
1449
+ data_description = self.validate_data_description("draw_cubic_curve",data_description)
1450
+ self.validate_data("draw_cubic_curve",data)
1451
+ graph_id = 0
1452
+ id = 0
1453
+ color_id =0
1454
+
1455
+ data_description["values"].each do |col_name|
1456
+ if ( serie_name == "" || serie_name == col_name )
1457
+ x_in = []
1458
+ y_in =[]
1459
+ y_t = []
1460
+ u = []
1461
+ x_in[0] = 0
1462
+ y_in[0] = 0
1463
+
1464
+ data_description["description"].each do |key_i,value_i|
1465
+ if ( key_i == col_name )
1466
+ color_id = id
1467
+ id = id+1
1468
+ end
1469
+ end
1470
+ index = 1
1471
+ x_last = -1
1472
+ missing = []
1473
+ data.each do |key|
1474
+ if(!key[col_name].nil?)
1475
+ val = key[col_name]
1476
+
1477
+ x_in[index] = index
1478
+ #y_in[index] = val
1479
+ #my hack TODO "" convet missing values to zero
1480
+ y_in[index] = val if ((val).is_a?(Numeric))
1481
+ y_in[index] = 0 if (!(val).is_a?(Numeric))
1482
+ ######
1483
+ missing[index]=true if (!(val).is_a?(Numeric))
1484
+ index=index+1
1485
+ end
1486
+ end
1487
+ index= index-1
1488
+ y_t[0] = 0
1489
+ y_t[1] = 0
1490
+ u[0] = 0
1491
+ u[1] = 0
1492
+ i =2
1493
+ y_last =0
1494
+
1495
+ while(i<=index-1)
1496
+ sig = (x_in[i]-x_in[i-1])*1.0/(x_in[i+1]-x_in[i-1]) #rescue 0
1497
+ p=sig*y_t[i-1]+2
1498
+ y_t[i]=(sig-1)/p
1499
+ u[i]=(y_in[i+1]-y_in[i])*1.0/(x_in[i+1]-x_in[i])-(y_in[i]-y_in[i-1])*1.0/(x_in[i]-x_in[i-1]) #rescue 0
1500
+ u[i]=(6*u[i]/(x_in[i+1]-x_in[i-1])-sig*u[i-1])/p #rescue 0
1501
+ i=i+1
1502
+ end
1503
+ qn = 0
1504
+ un = 0
1505
+ y_t[index] = (un - qn * u[index-1]) / (qn * y_t[index-1] + 1)
1506
+ k = index-1
1507
+ while k>=1
1508
+ y_t[k]=y_t[k]* y_t[k+1]+u[k]
1509
+ k=k-1
1510
+ end
1511
+ x_pos = @g_area_x1 + @g_area_x_offset
1512
+ x =1
1513
+ while x<=index
1514
+ klo=1
1515
+ khi=index
1516
+ k = khi-klo
1517
+ while k>1
1518
+ k=khi-klo
1519
+ if x_in[k]>=x
1520
+ khi=k
1521
+ else
1522
+ klo=k
1523
+ end
1524
+ end
1525
+ klo=khi-1
1526
+ h = x_in[khi]-x_in[klo]
1527
+ a = (x_in[khi]-x)/h rescue 1
1528
+ b = (x-x_in[klo])/h rescue 1
1529
+ value = a*y_in[klo]+b*y_in[khi]+((a*a*a-a)*y_t[klo]+(b*b*b-b)*y_t[khi])*(h*h)/6
1530
+ y_pos = @g_area_y2-((value-@vmin)*@division_ratio)
1531
+ #TODO Check(x_last!=-1 && !missing[x.floor].nil? && !missing[(x+1).floor].nil? )
1532
+ #UPDATED
1533
+ if (x_last!=-1 && missing[x.floor].nil? && missing[(x+1).floor].nil? )
1534
+ self.draw_line(x_last,y_last,x_pos,y_pos, @palette[id]["r"],@palette[id]["g"],@palette[id]["b"],true)
1535
+ end
1536
+ x_last = x_pos
1537
+ y_last = y_pos
1538
+ x_pos = x_pos +@division_width*accuracy
1539
+ x=x+accuracy
1540
+ end
1541
+ #Add potentialy missing values
1542
+ x_pos = x_pos - @division_width * accuracy
1543
+ if ( x_pos < (@g_area_x2 - @g_area_x_offset) )
1544
+ y_pos = @g_area_y2 - ((y_in[index]-@vmin) * @division_ratio)
1545
+ self.draw_line(x_last,y_last,@g_area_x2-@g_area_x_offset,y_pos,@palette[id]["r"],@palette[id]["g"],@palette[id]["b"],true)
1546
+ end
1547
+ graph_id += 1
1548
+ end
1549
+ end
1550
+ end
1551
+ # This function will draw a filled curved line graph using all the registered series.
1552
+ # This curve is using a cubic algorythm to process the average values between two points.
1553
+ # You have to specify the accuracy between two points, typicaly a 0.1 value is acceptable. the smaller the value is, the longer it will take to process the graph.
1554
+ # You can provide the alpha value used when merging all series layers.
1555
+ # If around_zero is set to true, the area drawn will be between the 0 axis and the line graph value.
1556
+
1557
+ def draw_filled_cubic_curve(data,data_description,accuracy=0.1,alpha=100,around_zero=false)
1558
+ data_description = self.validate_data_description("draw_filled_cubic_curve",data_description)
1559
+ self.validate_data("draw_filled_cubic_curve",data)
1560
+ layer_width = @g_area_x2-@g_area_x1
1561
+ layer_height = @g_area_y2-@g_area_y1
1562
+ y_zero = layer_height - ((0-@vmin) * @division_ratio)
1563
+ y_zero = layer_height if ( y_zero > layer_height )
1564
+ graph_id = 0
1565
+ id = 0
1566
+ color_id =0
1567
+ data_description["values"].each do |col_name|
1568
+ x_in = []
1569
+ y_in =[]
1570
+ y_t = []
1571
+ u = []
1572
+ x_in[0] = 0
1573
+ y_in[0] = 0
1574
+ data_description["description"].each do |key_i,value_i|
1575
+ if ( key_i == col_name )
1576
+ color_id = id
1577
+ id = id+1
1578
+ end
1579
+ end
1580
+ index = 1
1581
+ x_last = -1
1582
+ missing = []
1583
+ data.each do |key|
1584
+ if(!key[col_name].nil?)
1585
+ val = key[col_name]
1586
+ x_in[index] = index
1587
+ y_in[index] = val
1588
+ missing[index]=true if ((val).is_a?(Numeric))
1589
+ index=index+1
1590
+ end
1591
+ end
1592
+ index= index-1
1593
+ y_t[0] = 0
1594
+ y_t[1] = 0
1595
+ u[1] = 0
1596
+ i =2
1597
+ y_last =0
1598
+
1599
+ while(i<index)
1600
+ sig = (x_in[i]-x_in[i-1])*1.0/(x_in[i+1]-x_in[i-1]) #rescue 0
1601
+ p=sig*y_t[i-1]+2
1602
+ y_t[i]=(sig-1)/p
1603
+ u[i]=(y_in[i+1]-y_in[i])*1.0/(x_in[i+1]-x_in[i])-(y_in[i]-y_in[i-1])*1.0/(x_in[i]-x_in[i-1]) #rescue 0
1604
+ u[i]=(6*u[i]/(x_in[i+1]-x_in[i-1])-sig*u[i-1])/p #rescue 0
1605
+ i=i+1
1606
+ end
1607
+ qn = 0
1608
+ un = 0
1609
+ y_t[index] = (un - qn * u[index-1]) / (qn * y_t[index-1] + 1)
1610
+ k = index-1
1611
+ while k>=1
1612
+ y_t[k]=y_t[k]* y_t[k+1]+u[k]
1613
+ k=k-1
1614
+ end
1615
+ points = []
1616
+ points << @g_area_x_offset
1617
+ points << layer_height
1618
+ @layers[0] = image_create_true_color(layer_width,layer_height)
1619
+ image_filled_rectangle(@layers[0],0,0,layer_width,layer_height, 255,255,255)
1620
+ image_color_transparent(@layers[0], 255,255,255)
1621
+ y_last = nil
1622
+ x_pos = @g_area_x_offset
1623
+ points_count= 2
1624
+ x=1
1625
+ while(x<=index)
1626
+ klo=1
1627
+ khi=index
1628
+ k = khi-klo
1629
+ while k>1
1630
+ k=khi-klo
1631
+ if x_in[k]>=x
1632
+ khi=k
1633
+ else
1634
+ klo=k
1635
+ end
1636
+ end
1637
+ klo=khi-1
1638
+ h = x_in[khi]-x_in[klo]
1639
+ a = (x_in[khi]-x)/h rescue 1
1640
+ b = (x-x_in[klo])/h rescue 1
1641
+ value = a*y_in[klo]+b*y_in[khi]+((a*a*a-a)*y_t[klo]+(b*b*b-b)*y_t[khi])*(h*h)/6
1642
+ y_pos = layer_height - ((value-@vmin) * @division_ratio);
1643
+
1644
+ a_points = []
1645
+ if ( !y_last.nil? && around_zero && (missing[x.floor].nil?) && (missing[(x+1).floor].nil?))
1646
+
1647
+ a_points << x_last
1648
+ a_points << y_last
1649
+ a_points << x_pos
1650
+ a_points << y_pos
1651
+ a_points << x_pos
1652
+ a_points << y_zero
1653
+ a_points << x_last
1654
+ a_points << y_zero
1655
+ #check No of points here 4 is pass check in image filled_polygon
1656
+ image_filled_polygon(@layers[0], a_points, @palette[color_id]["r"],@palette[color_id]["g"],@palette[color_id]["b"],4)
1657
+ end
1658
+ if ( missing[(x.floor)].nil? || y_last.nil?)
1659
+ points_count = points_count+1
1660
+ points << x_pos
1661
+ points << y_pos
1662
+ else
1663
+ points_count = points_count+1
1664
+ points << x_last
1665
+ points << y_last
1666
+ end
1667
+ y_last = y_pos
1668
+ x_last = x_pos
1669
+ x_pos = x_pos + @division_width * accuracy
1670
+ x=x+accuracy
1671
+ end
1672
+
1673
+ #// Add potentialy missing values
1674
+ # a_points = []
1675
+ x_pos = x_pos - @division_width * accuracy
1676
+ if ( x_pos < (layer_width-@g_area_x_offset) )
1677
+ y_pos = layer_height - ((y_in[index]-@vmin) * @division_ratio)
1678
+ if ( !y_last.nil? && around_zero )
1679
+ a_points << x_last
1680
+ a_points << y_last
1681
+ a_points << (layer_width-@g_area_x_offset)
1682
+ a_points << y_pos
1683
+ a_points << (layer_width-@g_area_x_offset)
1684
+ a_points << y_zero
1685
+ a_points << x_last
1686
+ a_points << y_zero
1687
+ # imagefilledpolygon(@layers[0],a_points,4,$C_Graph);
1688
+ image_filled_polygon(@layers[0], a_points, @palette[color_id]["r"],@palette[color_id]["g"],@palette[color_id]["b"],4)
1689
+ end
1690
+
1691
+ if ( y_in[klo] != "" && y_in[khi] != "" || y_last.nil? )
1692
+
1693
+ points_count +=1
1694
+ points << (layer_width-@g_area_x_offset).floor
1695
+ points << (y_pos).floor
1696
+ end
1697
+ end
1698
+
1699
+ points << (layer_width-@g_area_x_offset).floor
1700
+ points << layer_height.floor
1701
+
1702
+ if ( !around_zero )
1703
+ image_filled_polygon(@layers[0], points, @palette[color_id]["r"],@palette[color_id]["g"],@palette[color_id]["b"],points_count)
1704
+ end
1705
+
1706
+ image_copy_merge(@layers[0],@picture,@g_area_x1,@g_area_y1,0,0,layer_width,layer_height,alpha);
1707
+ image_destroy(@layers[0])
1708
+
1709
+ self. draw_cubic_curve(data, data_description,accuracy,col_name)
1710
+ graph_id+=1
1711
+ end
1712
+ end
1713
+
1714
+ # This function will draw a filled line graph using all the registered series.
1715
+ # You can provide the alpha value used when merging all series layers.
1716
+ # If around_zero is set to true, the area drawn will be between the 0 axis and the line graph value.
1717
+
1718
+ def draw_filled_line_graph(data,data_description,alpha=100,around_zero=false)
1719
+ empty = -2147483647
1720
+ data_description = self.validate_data_description("draw_filled_line_graph",data_description)
1721
+ self.validate_data("draw_filled_line_graph",data)
1722
+ layer_width = @g_area_x2-@g_area_x1
1723
+ layer_height = @g_area_y2-@g_area_y1
1724
+ graph_id = 0
1725
+ id =0
1726
+ color_id =0
1727
+ data_description["values"].each do |col_name|
1728
+ data_description["description"].each do |key_i,value_i|
1729
+ if ( key_i == col_name )
1730
+ color_id = id
1731
+ id = id+1
1732
+ end
1733
+ end
1734
+
1735
+ a_points = []
1736
+ a_points << @g_area_x_offset
1737
+ a_points << layer_height
1738
+ @layers[0] = image_create_true_color(layer_width,layer_height)
1739
+ c_white = allocate_color(@layers[0],255,255,255)
1740
+ image_filled_rectangle(@layers[0],0,0,layer_width,layer_height,255,255,255)
1741
+ image_color_transparent(@layers[0],255,255,255)
1742
+
1743
+ xpos = @g_area_x_offset
1744
+ xlast = -1
1745
+ points_count = 2
1746
+ y_zero = layer_height - ((0-@vmin) * @division_ratio)
1747
+ y_zero = layer_height if ( y_zero > layer_height )
1748
+ ylast = empty
1749
+
1750
+ data.each do |key|
1751
+ value = key[col_name]
1752
+ if key[col_name].is_a?(Numeric)
1753
+ ypos = layer_height - ((value-@vmin) * @division_ratio)
1754
+ else
1755
+ ypos = layer_height - ((0-@vmin) * @division_ratio)
1756
+ end
1757
+ # Save point into the image map if option activated */
1758
+ if ( @build_map )
1759
+ #self.add_to_image_map(xpos-3,ypos-3,xpos+3,ypos+3,data_description["description"][col_name],key[col_name].data_description["unit"]["Y"],"FLine");
1760
+ end
1761
+ if ( !(value.is_a?(Numeric)))
1762
+ points_count+=1
1763
+ a_points << xlast
1764
+ a_points << layer_height
1765
+ ylast = empty
1766
+ else
1767
+ points_count+=1
1768
+ if ( ylast != empty )
1769
+ a_points << xpos
1770
+ a_points << ypos
1771
+ else
1772
+ points_count+=1
1773
+ a_points << xpos
1774
+ a_points << layer_height
1775
+ a_points << xpos
1776
+ a_points << ypos
1777
+ end
1778
+
1779
+ if (ylast !=empty && around_zero)
1780
+ points = []
1781
+ points << xlast
1782
+ points << ylast
1783
+ points << xpos
1784
+ points << ypos
1785
+ points << xpos
1786
+ points << y_zero
1787
+ points << xlast
1788
+ points << y_zero
1789
+ c_graph = allocate_color(@layers[0],@palette[color_id]["r"],@palette[color_id]["g"],@palette[color_id]["b"])
1790
+ image_filled_polygon(@layers[0],points,@palette[color_id]["r"],@palette[color_id]["g"],@palette[color_id]["b"],4)
1791
+ end
1792
+ ylast = ypos
1793
+ end
1794
+ xlast = xpos;
1795
+ xpos = xpos + @division_width
1796
+ end
1797
+
1798
+ a_points << layer_width - @g_area_x_offset
1799
+ a_points << layer_height;
1800
+
1801
+ if ( around_zero == false )
1802
+ c_graph = allocate_color(@layers[0],@palette[color_id]["r"],@palette[color_id]["g"],@palette[color_id]["b"])
1803
+ image_filled_polygon(@layers[0],a_points,@palette[color_id]["r"],@palette[color_id]["g"],@palette[color_id]["b"],points_count);
1804
+ end
1805
+
1806
+ image_copy_merge(@layers[0],@picture,@g_area_x1,@g_area_y1,0,0,layer_width,layer_height,alpha);
1807
+ image_destroy(@layers[0])
1808
+ graph_id+=1
1809
+ self.draw_line_graph(data,data_description,col_name)
1810
+ end
1811
+
1812
+ end
1813
+ # This function will draw a bar graph using all the registered series.
1814
+ # When creating a bar graph, don't forget to set the with_margin parameter of the draw_scale function to true.
1815
+ # Setting shadow to true will draw a shadow behind each series, this will also slow down a bit the renderer engine.
1816
+
1817
+ def draw_bar_graph(data,data_description,shadow=false,alpha=100)
1818
+ data_description = self.validate_data_description("drawBarGraph",data_description)
1819
+ self.validate_data("drawBarGraph",data)
1820
+
1821
+ graph_id = 0
1822
+ series = (data_description["values"]).count
1823
+ series_width = @division_width / (series+1)
1824
+ serie_x_offset = @division_width / 2 - series_width / 2
1825
+
1826
+ y_zero = @g_area_y2 - ((0-@vmin) * @division_ratio)
1827
+ y_zero = @g_area_y2 if ( y_zero> @g_area_y2 )
1828
+ serie_id = 0
1829
+ color_id =0
1830
+ id = 0
1831
+ data_description["values"].each do |col_name|
1832
+ data_description["description"].each do |key_i,value_i|
1833
+ if ( key_i == col_name )
1834
+ color_id = id
1835
+ id = id+1
1836
+ end
1837
+ end
1838
+ x_pos = @g_area_x1 + @g_area_x_offset - serie_x_offset + series_width * serie_id
1839
+ x_last = -1
1840
+ data.each do |key|
1841
+ if ( !key[col_name].nil?)
1842
+ if ( key[col_name].is_a?(Numeric) )
1843
+ value = key[col_name]
1844
+ y_pos = @g_area_y2 - ((value-@vmin) * @division_ratio)
1845
+ # Save point into the image map if option activated */
1846
+ if (@build_map )
1847
+ #self.add_to_image_map(x_pos+1,[y_zero,y_pos].min,x_pos+series_width-1,[y_zero,y_pos].max,data_description["description"][col_name],data[key][col_name].data_description["unit"]["y"],"Bar");
1848
+ end
1849
+ if ( shadow && alpha == 100 )
1850
+ self.draw_rectangle(x_pos+1,y_zero,x_pos+series_width-1,y_pos,25,25,25)
1851
+ end
1852
+ self.draw_filled_rectangle(x_pos+1,y_zero,x_pos+series_width-1,y_pos,@palette[color_id]["r"],@palette[color_id]["g"],@palette[color_id]["b"],true,alpha)
1853
+ end
1854
+ x_pos = x_pos + @division_width
1855
+ end
1856
+ end
1857
+ serie_id = serie_id+1
1858
+ end
1859
+ end
1860
+
1861
+ # This function will draw a stacked bar graph using all the registered series.
1862
+ # When creating a bar graph, don't forget to set the with_margin parameter of the draw_scale function to true.
1863
+ # Don't forget to change the automatic scaling to Rchart::SCALE_ADDALL to have an accurate scaling mode.
1864
+ # You can specify the transparency and if the bars must be contiguous or with space (default)
1865
+ def draw_stacked_bar_graph(data,data_description,alpha=50,contiguous=false)
1866
+ # /* Validate the Data and data_description array */
1867
+ data_description = self.validate_data_description("draw_bar_graph",data_description)
1868
+ self.validate_data("draw_bar_graph",data)
1869
+ graph_id = 0
1870
+ series = (data_description["values"].count)
1871
+ if ( contiguous )
1872
+ series_width = @division_width
1873
+ else
1874
+ series_width = @division_width * 0.8;
1875
+ end
1876
+ y_zero = @g_area_y2 - ((0-@vmin) * @division_ratio)
1877
+ y_zero = @g_area_y2 if ( y_zero > @g_area_y2 )
1878
+ series_id = 0
1879
+ last_value = {}
1880
+ id = 0
1881
+ color_id = 0
1882
+ data_description["values"].each do |col_name|
1883
+ data_description["description"].each do |key_i,value_i|
1884
+ if ( key_i == col_name )
1885
+ color_id = id
1886
+ id = id+1
1887
+ end
1888
+ end
1889
+ x_pos = @g_area_x1 + @g_area_x_offset - series_width / 2
1890
+ x_last = -1
1891
+ data.each do |key|
1892
+ if ( !key[col_name].nil?)
1893
+ if ( key[col_name].is_a?(Numeric) )
1894
+ value = key[col_name]
1895
+ if (!last_value[key].nil?)
1896
+ y_pos = @g_area_y2 - (((value+last_value[key])-@vmin) * @division_ratio)
1897
+ y_bottom = @g_area_y2 - ((last_value[key]-@vmin) * @division_ratio)
1898
+ last_value[key] += value
1899
+ else
1900
+ y_pos = @g_area_y2 - ((value-@vmin) * @division_ratio)
1901
+ y_bottom = y_zero
1902
+ last_value[key] = value
1903
+ end
1904
+ # Save point into the image map if option activated
1905
+ if ( @build_map )
1906
+ #self.add_to_image_map(x_pos+1,[y_bottom,y_pos].min,x_pos+series_width-1,[y_bottom,y_pos].max,data_description["description"][col_name],data[key][col_name].data_description["unit"]["y"],"sBar");
1907
+ end
1908
+ self.draw_filled_rectangle(x_pos+1,y_bottom,x_pos+series_width-1,y_pos,@palette[color_id]["r"],@palette[color_id]["g"],@palette[color_id]["b"],true,alpha)
1909
+ end
1910
+ end
1911
+ x_pos = x_pos + @division_width
1912
+ end
1913
+ series_id+=1
1914
+ end
1915
+ end
1916
+ # This function will draw a superposed bar graph using all the registered series.
1917
+ # You can provide the alpha value used when merging all series layers.
1918
+
1919
+ def draw_overlay_bar_graph(data,data_description,alpha=50)
1920
+ data_description = self.validate_data_description("draw_overlay_bar_graph",data_description)
1921
+ self.validate_data("draw_overlay_bar_graph",data)
1922
+ layer_width = @g_area_x2-@g_area_x1
1923
+ layer_height = @g_area_y2-@g_area_y1
1924
+ graph_id = 0
1925
+ color_id =0
1926
+ id =0
1927
+ data_description["values"].each do |col_name|
1928
+ data_description["description"].each do |key_i,value_i|
1929
+ if ( key_i == col_name )
1930
+ color_id = id
1931
+ id = id+1
1932
+ end
1933
+ end
1934
+ @layers[graph_id] = image_create_true_color(layer_width,layer_height)
1935
+ image_filled_rectangle(@layers[graph_id],0,0,layer_width,layer_height,255,255,255)
1936
+ image_color_transparent(@layers[graph_id],255,255,255)
1937
+ x_width = @division_width / 4
1938
+ x_pos = @g_area_x_offset
1939
+ y_zero = layer_height - ((0-@vmin) * @division_ratio)
1940
+ x_last = -1
1941
+ points_count = 2
1942
+ data.each do |key|
1943
+ if(!key[col_name].nil?)
1944
+ if(key[col_name].is_a?(Numeric))
1945
+ value = key[col_name]
1946
+ if (value.is_a?(Numeric) )
1947
+ y_pos = layer_height - ((value-@vmin) * @division_ratio)
1948
+ image_filled_rectangle(@layers[graph_id],x_pos-x_width,y_pos,x_pos+x_width,y_zero,@palette[graph_id]["r"],@palette[graph_id]["g"],@palette[graph_id]["b"])
1949
+ x1 = (x_pos - x_width + @g_area_x1).floor
1950
+ y1 = (y_pos+@g_area_y1).floor + 0.2
1951
+ x2 = (x_pos + x_width + @g_area_x1).floor
1952
+ y2 = @g_area_y2 - ((0-@vmin) * @division_ratio)
1953
+ x1 = @g_area_x1 + 1 if ( x1 <= @g_area_x1 )
1954
+ x2 = @g_area_x2 - 1 if ( x2 >= @g_area_x2 )
1955
+
1956
+ # Save point into the image map if option activated */
1957
+ if ( @build_map )
1958
+ #self.add_to_image_map(x1,[y1,y2].min,x2,[y1,y2].max,data_description["description"][col_name],data[key][col_name].data_description["unit"]["y"],"oBar");
1959
+ end
1960
+ self.draw_line(x1,y1,x2,y1,@palette[color_id]["r"],@palette[color_id]["g"],@palette[color_id]["b"],true)
1961
+ end
1962
+ end
1963
+ end
1964
+ x_pos = x_pos + @division_width
1965
+ end
1966
+ graph_id+=1
1967
+ end
1968
+ i=0
1969
+ while (i<=(graph_id-1))
1970
+ image_copy_merge(@layers[i],@picture,@g_area_x1,@g_area_y1,0,0,layer_width,layer_height,alpha)
1971
+ image_destroy(@layers[i])
1972
+ i=i+1
1973
+ end
1974
+ end
1975
+
1976
+ # This function will draw the minimum & maximum values for a specific point using all the registered series
1977
+ # You can optionaly specify the vertical line color.
1978
+
1979
+ def draw_limits_graph(data,data_description,r=0,g=0,b=0)
1980
+ data_description = self.validate_data_description("draw_limits_graph",data_description)
1981
+ self.validate_data("draw_limits_graph",data)
1982
+ x_width = @division_width / 4
1983
+ xpos = @g_area_x1 + @g_area_x_offset
1984
+ data.each do |key|
1985
+ min = key[data_description["values"][0]]
1986
+ max = key[data_description["values"][0]]
1987
+ graph_id = 0
1988
+ max_id = 0
1989
+ min_id = 0
1990
+ data_description["values"].each do |col_name|
1991
+ if (!key[col_name].nil?)
1992
+ if ( key[col_name] > max && key[col_name].is_a?(Numeric))
1993
+ max = key[col_name]
1994
+ max_id = graph_id
1995
+ end
1996
+ end
1997
+ if ( !key[col_name].nil? && key[col_name].is_a?(Numeric))
1998
+ if ( key[col_name] < min )
1999
+ min = key[col_name]
2000
+ min_id = graph_id
2001
+ end
2002
+ graph_id+=1
2003
+ end
2004
+ end
2005
+
2006
+ ypos = @g_area_y2 - ((max-@vmin) * @division_ratio)
2007
+ x1 = (xpos - x_width).floor
2008
+ y1 = (ypos).floor - 0.2
2009
+ x2 = (xpos + x_width).floor
2010
+ x1 = @g_area_x1 + 1 if ( x1 <= @g_area_x1 )
2011
+ x2 = @g_area_x2 - 1 if ( x2 >= @g_area_x2 )
2012
+ ypos = @g_area_y2 - ((min-@vmin) * @division_ratio)
2013
+ y2 = ypos.floor + 0.2
2014
+ self.draw_line(xpos.floor-0.2,y1+1,xpos.floor-0.2,y2-1,r,g,b,true)
2015
+ self.draw_line(xpos.floor+0.2,y1+1,xpos.floor+0.2,y2-1,r,g,b,true)
2016
+ self.draw_line(x1,y1,x2,y1,@palette[max_id]["r"],@palette[max_id]["g"],@palette[max_id]["b"],false)
2017
+ self.draw_line(x1,y2,x2,y2,@palette[min_id]["r"],@palette[min_id]["g"],@palette[min_id]["b"],false)
2018
+ xpos = xpos + @division_width
2019
+ end
2020
+ end
2021
+
2022
+ # This function will draw a classical non-exploded pie chart.
2023
+ # * To do so you must specify the data & data_description array.Only one serie of data is allowed for pie graph.
2024
+ # * You can associate a description of each value in another serie by marking it using the set_abscise_label_serie function.
2025
+ # * You must specify the center position of the chart. You can also optionally specify the radius of the pie and if the percentage should be printed.
2026
+ # * r,g,b can be used to set the color of the line that will surround each pie slices.
2027
+ # * You can specify the number of decimals you want to be displayed in the labels (default is 0 )
2028
+ # By default no labels are written around the pie chart. You can use the following modes for the draw_labels parameter
2029
+ # * Rchart:: PIE_NOLABEL No labels displayed
2030
+ # * Rchart:: PIE_PERCENTAGE Percentages are displayed
2031
+ # * Rchart:: PIE_LABELS Series labels displayed
2032
+ # * Rchart:: PIE_PERCENTAGE_LABEL Series labels & percentage displayed
2033
+ # This will draw a pie graph centered at (150-150) with a radius of 100, no labels
2034
+ # * chart.draw_basic_pie_graph(chart_data.get_data,chart_data.get_data_description,150,150)
2035
+ # This will draw a pie graph centered at (150-150) with a radius of 50 and percentages
2036
+ # * chart.draw_basic_pie_graph(chart_data.get_data,chart_data.get_data_description,150,150,50,Rchart::PIE_PERCENTAGE)
2037
+ # This will draw a pie graph centered at (150-150) with a radius of 100, captions and black borders
2038
+ # * chart.draw_basic_pie_graph(chart_data.get_data,chart_data.get_data_description,150,150,100,Rchart::PIE_PERCENTAGE,0,0,0)
2039
+ def draw_basic_pie_graph(data,data_description,x_pos,y_pos,radius=100,draw_labels=PIE_NOLABEL,r=255,g=255,b=255,decimals=0)
2040
+ data_description = self.validate_data_description("draw_basic_pie_graph",data_description,false)
2041
+ self.validate_data("drawBasicPieGraph",data)
2042
+ # Determine pie sum
2043
+ series = 0
2044
+ pie_sum = 0
2045
+ i_values = []
2046
+ r_values = []
2047
+ i_labels = []
2048
+ data_description["values"].each do|col_name|
2049
+ if (col_name != data_description["position"])
2050
+ series = series+1
2051
+ data.each do |key|
2052
+ if (!key[col_name].nil?)
2053
+ pie_sum = pie_sum + key[col_name]
2054
+ i_values << key[col_name]
2055
+ i_labels << key[data_description["position"]]
2056
+ end
2057
+ end
2058
+ end
2059
+ end
2060
+
2061
+
2062
+ # Validate serie
2063
+ if ( series != 1 )
2064
+ raise_fatal("Pie chart can only accept one serie of data.");
2065
+ end
2066
+ splice_ratio = 360.0 / pie_sum
2067
+ splice_percent = 100.0 / pie_sum
2068
+
2069
+ #Calculate all polygons
2070
+ angle = 0
2071
+ top_plots = []
2072
+ i_values.each_with_index do |value,key|
2073
+
2074
+ top_plots[key]= [x_pos]
2075
+ top_plots[key]<< y_pos
2076
+ # Process labels position & size
2077
+ caption = "";
2078
+ if ( !(draw_labels == PIE_NOLABEL) )
2079
+ t_angle = angle+(value*splice_ratio/2)
2080
+ if (draw_labels == PIE_PERCENTAGE)
2081
+ caption = ((value * (10**decimals) * splice_percent)/(10**decimals)).round.to_s+"%"
2082
+ elsif (draw_labels == PIE_LABELS)
2083
+ caption = i_labels[key]
2084
+ elsif (draw_labels == PIE_PERCENTAGE_LABEL)
2085
+ caption = i_labels[key].to_s+"\r\n"+"."+((value * (10**decimals) * splice_percent)/(10**decimals)).round.to_s+"%";
2086
+ elsif (draw_labels == PIE_PERCENTAGE_LABEL)
2087
+ caption = i_labels[key].to_s+"\r\n"+"."+((value * (10**decimals) * splice_percent)/(10**decimals)).round.to_s+"%";
2088
+ end
2089
+ position = image_ftb_box(@font_size,0,@font_name,caption)
2090
+ text_width = position[2]-position[0]
2091
+ text_height = (position[1].abs)+(position[3].abs)
2092
+
2093
+ tx = Math.cos((t_angle) * Math::PI / 180 ) * (radius+10) + x_pos
2094
+
2095
+ if ( t_angle > 0 && t_angle < 180 )
2096
+ ty = Math.sin((t_angle) * Math::PI / 180 ) * (radius+10) + y_pos + 4
2097
+ else
2098
+ ty = Math.sin((t_angle) * Math::PI / 180 ) * (radius+4) + y_pos - (text_height/2)
2099
+ end
2100
+ tx = tx - text_width if ( t_angle > 90 && t_angle < 270 )
2101
+
2102
+ c_text_color = allocate_color(@picture,70,70,70);
2103
+ image_ttf_text(@picture,@font_size,0,tx,ty,c_text_color,@font_name,caption)
2104
+ end
2105
+ # Process pie slices
2106
+ i_angle = angle
2107
+ while(i_angle <=angle+value*splice_ratio)
2108
+
2109
+ top_x = (Math.cos(i_angle * Math::PI / 180 )) * radius + x_pos
2110
+ top_y = (Math.sin(i_angle * Math::PI/ 180 )) * radius + y_pos
2111
+ top_plots[key] << (top_x)
2112
+ top_plots[key] <<(top_y)
2113
+ i_angle = i_angle+0.5
2114
+ end
2115
+ top_plots[key]<< x_pos
2116
+ top_plots[key] << y_pos
2117
+ angle = i_angle
2118
+ end
2119
+ poly_plots = top_plots
2120
+ # Draw Top polygons
2121
+ poly_plots.each_with_index do |value,key|
2122
+ image_filled_polygon(@picture, poly_plots[key], @palette[key]["r"],@palette[key]["g"],@palette[key]["b"])
2123
+ end
2124
+ self. draw_circle(x_pos-0.5,y_pos-0.5,radius,r,g,b)
2125
+ self. draw_circle(x_pos-0.5,y_pos-0.5,radius+0.5,r,g,b)
2126
+ # Draw Top polygons
2127
+ top_plots.each_with_index do |value,key|
2128
+ j = 0
2129
+ while(j<=top_plots[key].count-4 )
2130
+ self.draw_line(top_plots[key][j],top_plots[key][j+1],top_plots[key][j+2],top_plots[key][j+3],r,g,b);
2131
+ j =j+2
2132
+ end
2133
+ end
2134
+ end
2135
+
2136
+ # This function will draw a 3D pie graph.
2137
+ # * To do so you must specify the data & data_description array.
2138
+ # * Only one serie of data is allowed for pie graph.
2139
+ # * You can associate a description of each value in another serie by marking it using the set_abscise_label_serie function. You must specify the center position of the chart.
2140
+ # * You can also optionally specify the radius of the pie, if the percentage should be printed, the 3D skew factor and the height of all splices.
2141
+ # * If enhance_colors is set to true, pie edges will be enhanced.
2142
+ # * If splice_distance is greated than 0, the pie will be exploded.
2143
+ # * You can specify the number of decimals you want to be displayed in the labels (default is 0 ).
2144
+ # By default no labels are written around the pie chart. You can use the following modes for the draw_labels parameter:
2145
+ # * Rchart:: PIE_NOLABEL No labels displayed
2146
+ # * Rchart:: PIE_PERCENTAGE Percentages are displayed
2147
+ # * Rchart:: PIE_LABELS Series labels displayed
2148
+ # * Rchart:: PIE_PERCENTAGE_LABEL Series labels & percentage displayed
2149
+ # This will draw a pie graph centered at (150-150) with a radius of 100, no labels
2150
+ # * chart.draw_pie_graph(chart_data.get_data,chart_data.get_data_description,150,150)
2151
+ # This will draw a pie graph centered at (150-150) with a radius of 50 and percentages
2152
+ # * chart.draw_pie_graph(chart_data.get_data,chart_data.get_data_description,150,150,50,Rchart::PIE_PERCENTAGE)
2153
+ # This will draw a pie graph centered at (150-150) with a radius of 100, captions and a skew factor of 30
2154
+ # * chart.draw_pie_graph(chart_data.get_data,chart_data.get_data_description,150,150,100,Rchart::PIE_PERCENTAGE,true,30)
2155
+ # This will draw a pie graph (..) exploded
2156
+ # * chart.draw_pie_graph(chart_data.get_data,chart_data.get_data_description,150,150,100,Rchart::PIE_PERCENTAGE,true,30,10,10)
2157
+ def draw_pie_graph(data,data_description,x_pos,y_pos,radius=100,draw_labels=PIE_NOLABEL,enhance_colors=true,skew=60,splice_height=20,splice_distance=0,decimals=0)
2158
+ data_description = self.validate_data_description("draw_pie_graph",data_description,false)
2159
+ self.validate_data("draw_pie_graph",data)
2160
+
2161
+ #Determine pie sum
2162
+ series = 0
2163
+ pie_sum= 0
2164
+ rpie_sum = 0
2165
+ i_values = []
2166
+ r_values = []
2167
+ i_labels = []
2168
+ series = 0
2169
+
2170
+ data_description["values"].each do|col_name|
2171
+ if (col_name != data_description["position"])
2172
+ series = series+1
2173
+ data.each do |key|
2174
+ if (!key[col_name].nil?)
2175
+ if (key[col_name] == 0)
2176
+ i_values << 0
2177
+ r_values << 0
2178
+ i_labels << 0
2179
+ i_labels<< key[data_description["position"]]
2180
+ else
2181
+ pie_sum+= key[col_name]
2182
+ i_values << key[col_name]
2183
+ i_labels << key[data_description["position"]]
2184
+ r_values << key[col_name]
2185
+ rpie_sum += key[col_name]
2186
+ end
2187
+ end
2188
+ end
2189
+ end
2190
+ end
2191
+
2192
+ # Validate serie
2193
+
2194
+ #RaiseFatal("Pie chart can only accept one serie of data.");
2195
+ #puts "Error Pie chart can only accept one serie of data." if ( series != 1 )
2196
+ splice_distance_ratio = splice_distance
2197
+ skew_height = (radius * skew) / 100;
2198
+ splice_ratio = ((360 - splice_distance_ratio *i_values.count*1.0) / pie_sum)
2199
+ splice_percent = 100.0 / pie_sum
2200
+ r_splice_percent = 100.0 / rpie_sum
2201
+ #Calculate all polygons
2202
+ angle = 0
2203
+ c_dev = 5;
2204
+ top_plots = []
2205
+ bot_plots = []
2206
+ atop_plots = []
2207
+ abot_plots = []
2208
+ i_values.each_with_index do |value,key|
2209
+ x_cent_pos = Math.cos((angle-c_dev+(value*splice_ratio+splice_distance_ratio)/2) * 3.1418 / 180 ) * splice_distance + x_pos
2210
+ y_cent_pos = Math.sin((angle-c_dev+(value*splice_ratio+splice_distance_ratio)/2) * 3.1418 / 180 ) * splice_distance + y_pos
2211
+ x_cent_pos2 = Math.cos((angle+c_dev+(value*splice_ratio+splice_distance_ratio)/2) * 3.1418 / 180 ) * splice_distance + x_pos
2212
+ y_cent_pos2 = Math.sin((angle+c_dev+(value*splice_ratio+splice_distance_ratio)/2) * 3.1418 / 180 ) * splice_distance + y_pos
2213
+ top_plots[key] = [(x_cent_pos).round]
2214
+ bot_plots[key] = [(x_cent_pos).round]
2215
+ top_plots[key] << (y_cent_pos).round
2216
+ bot_plots[key] << (y_cent_pos + splice_height).round
2217
+ atop_plots[key] = [x_cent_pos]
2218
+ abot_plots[key] = [x_cent_pos]
2219
+ atop_plots[key] << y_cent_pos
2220
+ abot_plots[key] << y_cent_pos + splice_height
2221
+ # Process labels position & size
2222
+ caption = ""
2223
+ if ( !(draw_labels == PIE_NOLABEL) )
2224
+
2225
+ t_angle = angle+(value*splice_ratio/2)
2226
+ if (draw_labels == PIE_PERCENTAGE)
2227
+ caption = ((r_values[key] * (10**decimals) * r_splice_percent)/(10**decimals)).round.to_s+"%"
2228
+ elsif (draw_labels == PIE_LABELS)
2229
+ caption = i_labels[key]
2230
+ elsif (draw_labels == PIE_PERCENTAGE_LABEL)
2231
+ caption = i_labels[key].to_s+"\r\n"+(((value * 10**decimals) * splice_percent)/(10**decimals)).round.to_s+"%"
2232
+ end
2233
+ position = image_ftb_box(@font_size,0,@font_name,caption)
2234
+ text_width =position[2]-position[0]
2235
+ text_height = ( position[1]).abs+(position[3]).abs
2236
+
2237
+ tx = Math.cos((t_angle) * Math::PI / 180 ) * (radius + 10)+ x_pos
2238
+
2239
+ if ( t_angle > 0 && t_angle < 180 )
2240
+ ty = Math.sin((t_angle) * Math::PI / 180 ) * (skew_height + 10) + y_pos + splice_height + 4
2241
+ else
2242
+ ty = Math.sin((t_angle) * Math::PI / 180 ) * (skew_height + 4) + y_pos - (text_height/2)
2243
+ end
2244
+ if ( t_angle > 90 && t_angle < 270 )
2245
+ tx = tx - text_width
2246
+ end
2247
+ #c_text_color = $this->AllocateColor(@picture,70,70,70);
2248
+ c_text_color = allocate_color(@picture,70,70,70)
2249
+ image_ttf_text(@picture,@font_size,0,tx,ty,c_text_color,@font_name,caption)
2250
+ end
2251
+
2252
+ # Process pie slices
2253
+
2254
+ i_angle = angle
2255
+ i_angle = i_angle.to_f
2256
+
2257
+ while(i_angle <=angle+value*splice_ratio)
2258
+
2259
+ top_x = (Math.cos(i_angle * Math::PI / 180 )) * radius + x_pos
2260
+ top_y = (Math.sin(i_angle * Math::PI/ 180 )) * skew_height + y_pos
2261
+
2262
+ top_plots[key] << (top_x).round
2263
+ bot_plots[key] <<(top_x).round
2264
+ top_plots[key] <<(top_y).round
2265
+ bot_plots[key] << (top_y + splice_height).round
2266
+ atop_plots[key] << top_x
2267
+ abot_plots[key] << top_x
2268
+ atop_plots[key] << top_y
2269
+ abot_plots[key] << top_y + splice_height
2270
+ i_angle=i_angle+0.5
2271
+ end
2272
+ top_plots[key] << (x_cent_pos2).round
2273
+ bot_plots[key] << (x_cent_pos2).round
2274
+ top_plots[key] << (y_cent_pos2).round
2275
+ bot_plots[key] << (y_cent_pos2 + splice_height).round
2276
+ atop_plots[key] << x_cent_pos2
2277
+ abot_plots[key] << x_cent_pos2
2278
+ atop_plots[key] << y_cent_pos2
2279
+ abot_plots[key] << y_cent_pos2 + splice_height
2280
+ angle = i_angle + splice_distance_ratio
2281
+ end
2282
+
2283
+ # Draw Bottom polygons
2284
+ i_values.each_with_index do |val,key|
2285
+ c_graph_lo = allocate_color(@picture,@palette[key]["r"]-20,@palette[key]["g"]-20,@palette[key]["b"]-20)
2286
+ image_filled_polygon(@picture,bot_plots[key],@palette[key]["r"]-20,@palette[key]["g"]-20,@palette[key]["b"]-20)
2287
+ if (enhance_colors)
2288
+ en = -10
2289
+ else
2290
+ en = 0
2291
+ end
2292
+ j =0
2293
+ while(j<=(abot_plots[key].length)-4)
2294
+ self.draw_line(abot_plots[key][j],abot_plots[key][j+1],abot_plots[key][j+2],abot_plots[key][j+3],@palette[key]["r"]+en,@palette[key]["g"]+en,@palette[key]["b"]+en);
2295
+ j= j+2
2296
+ end
2297
+ end
2298
+
2299
+ # Draw pie layers
2300
+ if ( enhance_colors )
2301
+ color_ratio = 30 / splice_height
2302
+ else
2303
+ color_ratio = 25 / splice_height
2304
+ end
2305
+ i = splice_height-1
2306
+ while(i>=1)
2307
+ i_values.each_with_index do |val,key|
2308
+ c_graph_lo = allocate_color(@picture,@palette[key]["r"]-10,@palette[key]["g"]-10,@palette[key]["b"]-10)
2309
+ plots =[]
2310
+ plot = 0
2311
+ top_plots[key].each_with_index do |value2,key2|
2312
+ plot = plot+1
2313
+ if ( plot % 2 == 1 )
2314
+ plots << value2
2315
+ else
2316
+ plots << value2+i
2317
+ end
2318
+ end
2319
+ image_filled_polygon(@picture,plots,@palette[key]["r"]-10,@palette[key]["g"]-10,@palette[key]["b"]-10)
2320
+ index = (plots).count
2321
+ if (enhance_colors )
2322
+ color_factor = -20 + (splice_height - $i) * color_ratio
2323
+ else
2324
+ color_factor = 0
2325
+ end
2326
+
2327
+ self.draw_antialias_pixel(plots[0],plots[1],@palette[key]["r"]+color_factor,@palette[key]["g"]+color_factor,@palette[key]["b"]+color_factor);
2328
+ self.draw_antialias_pixel(plots[2],plots[3],@palette[key]["r"]+color_factor,@palette[key]["g"]+color_factor,@palette[key]["b"]+color_factor);
2329
+ self.draw_antialias_pixel(plots[index-4],plots[index-3],@palette[key]["r"]+color_factor,@palette[key]["g"]+color_factor,@palette[key]["b"]+color_factor);
2330
+ end
2331
+ i = i-1
2332
+ end
2333
+ #Draw Top polygons
2334
+ key = i_values.length-1
2335
+ while(key>=0)
2336
+ c_graph_lo = allocate_color(@picture,@palette[key]["r"],@palette[key]["g"],@palette[key]["b"])
2337
+ image_filled_polygon(@picture,top_plots[key],@palette[key]["r"],@palette[key]["g"],@palette[key]["b"])
2338
+
2339
+ if ( enhance_colors )
2340
+ en = 10
2341
+ else
2342
+ en = 0
2343
+ end
2344
+ j = 0
2345
+
2346
+ while(j<=(atop_plots[key]).length-4)
2347
+ self.draw_line(atop_plots[key][j],atop_plots[key][j+1],atop_plots[key][j+2],atop_plots[key][j+3],@palette[key]["r"]+en,@palette[key]["g"]+en,@palette[key]["b"]+en);
2348
+ j = j+2
2349
+ end
2350
+ key = key -1
2351
+ end
2352
+ end
2353
+ # This function is an alias of the draw_flat_pie_graph function.
2354
+ def draw_flat_pie_graph_with_shadow(data,data_description,x_pos,y_pos,radius=100,draw_labels=PIE_NOLABEL,splice_distance=0,decimals=0)
2355
+ self.draw_flat_pie_graph(data,data_description,x_pos+@shadow_x_distance,y_pos+@shadow_y_distance,radius,PIE_NOLABEL,splice_distance,decimals,true)
2356
+ self.draw_flat_pie_graph(data,data_description,x_pos,y_pos,radius,draw_labels,splice_distance,decimals,false)
2357
+ end
2358
+
2359
+ # This function will draw a flat 2D pie graph.
2360
+ # To do so you must specify the data & data_description array.
2361
+ # * Only one serie of data is allowed for pie graph.
2362
+ # * You can associate a description of each value in another serie by marking it using the set_abscise_label_serie function. You must specify the center position of the chart.
2363
+ # * You can also optionally specify the radius of the pie and if the percentage should be printed.
2364
+ # * If splice_distance is greated than 0, the pie will be exploded.
2365
+ # * You can specify the number of decimals you want to be displayed in the labels (default is 0 )
2366
+ # By default no labels are written around the pie chart. You can use the following modes for the draw_labels parameter:
2367
+ # * Rchart:: PIE_NOLABEL No labels displayed
2368
+ # * Rchart:: PIE_PERCENTAGE Percentages are displayed
2369
+ # * Rchart:: PIE_LABELS Series labels displayed
2370
+ # * Rchart:: PIE_PERCENTAGE_LABEL Series labels & percentage displayed
2371
+ # This will draw a pie graph centered at (150-150) with a radius of 100, no labels
2372
+ # * chart.draw_flat_pie_graph(chart_data.get_data,chart_data.get_data_description,150,150);
2373
+ # This will draw a pie graph centered at (150-150) with a radius of 50 and percentages
2374
+ # * chart.draw_flat_pie_graph(chart_data.get_data,chart_data.get_data_description,150,150,50,Rchart::PIE_PERCENTAGE)
2375
+ # This will draw a pie graph centered at (150-150) with a radius of 100, captions and slightly exploded
2376
+ # * chart.draw_flat_pie_graph(chart_data.get_data,chart_data.get_data_description,150,150,100,Rchart::PIE_PERCENTAGE,4)
2377
+
2378
+ def draw_flat_pie_graph(data,data_description,x_pos,y_pos,radius=100,draw_labels=PIE_NOLABEL,splice_distance=0,decimals=0,all_black=false)
2379
+ data_description = self.validate_data_description("draw_flat_pie_graph",data_description,false)
2380
+ self.validate_data("draw_flat_pie_graph",data)
2381
+ shadow_status = @shadow_active
2382
+ @shadow_active = false
2383
+ # Determine pie sum
2384
+ series = 0
2385
+ pie_sum = 0
2386
+ i_values = []
2387
+ r_values = []
2388
+ i_labels = []
2389
+ data_description["values"].each do|col_name|
2390
+ if (col_name != data_description["position"])
2391
+ series = series+1
2392
+ data.each do |key|
2393
+ if (!key[col_name].nil?)
2394
+ pie_sum = pie_sum + key[col_name]
2395
+ i_values << key[col_name]
2396
+ i_labels << key[data_description["position"]]
2397
+ end
2398
+ end
2399
+ end
2400
+ end
2401
+
2402
+ #Validate serie
2403
+ if ( series != 1 )
2404
+ raise_fatal("Pie chart can only accept one serie of data.");
2405
+ return -1
2406
+ end
2407
+
2408
+ splice_ratio = 360.0 / pie_sum
2409
+ splice_percent = 100.0 / pie_sum
2410
+ # Calculate all polygons
2411
+ angle = 0;
2412
+ top_plots = []
2413
+ i_values.each_with_index do |value,key|
2414
+ x_offset = Math.cos((angle+(value/2*splice_ratio)) * Math::PI / 180 ) * splice_distance
2415
+ y_offset = Math.sin((angle+(value/2*splice_ratio)) * Math::PI / 180 ) * splice_distance
2416
+
2417
+ top_plots[key] = [(x_pos + x_offset).round]
2418
+ top_plots[key] << (y_pos + y_offset).round
2419
+ if ( all_black )
2420
+ rc = @shadow_r_color
2421
+ gc = @shadow_g_color
2422
+ bc = @shadow_b_color
2423
+ else
2424
+ rc = @palette[key]["r"]
2425
+ gc = @palette[key]["g"]
2426
+ bc = @palette[key]["b"]
2427
+ end
2428
+ x_line_last = ""
2429
+ y_line_last = ""
2430
+ # Process labels position & size
2431
+ caption = ""
2432
+ if ( !(draw_labels == PIE_NOLABEL) )
2433
+ t_angle = angle+(value*splice_ratio/2)
2434
+ if (draw_labels == PIE_PERCENTAGE)
2435
+ caption = ((value * (10**decimals) * splice_percent)/(10**decimals)).round.to_s+"%"
2436
+ elsif (draw_labels == PIE_LABELS)
2437
+ caption = i_labels[key]
2438
+ elsif (draw_labels == PIE_PERCENTAGE_LABEL)
2439
+ caption = i_labels[key].to_s+".\r\n"+((value * (10**decimals) * splice_percent)/(10**decimals)).round.to_s+"%"
2440
+ elsif (draw_labels == PIE_PERCENTAGE_LABEL)
2441
+ caption = i_labels[key].to_s+".\r\n"+((value * (10**decimals) * splice_percent)/(10**decimals)).round.to_s+"%"
2442
+ end
2443
+ position = image_ftb_box(@font_size,0,@font_name,caption)
2444
+ text_width = position[2]-position[0]
2445
+ text_height = (position[1].abs)+(position[3].abs)
2446
+
2447
+ tx = Math.cos((t_angle) * Math::PI / 180 ) * (radius+10+splice_distance) + x_pos
2448
+ if ( t_angle > 0 && t_angle < 180 )
2449
+ ty = Math.sin((t_angle) * Math::PI / 180 ) * (radius+10+splice_distance) + y_pos + 4
2450
+ else
2451
+ ty = Math.sin((t_angle) * Math::PI / 180 ) * (radius+splice_distance+4) + y_pos - (text_height*1.0/2)
2452
+ end
2453
+ tx = tx - text_width if ( t_angle > 90 && t_angle < 270 )
2454
+ c_text_color = allocate_color(@picture,70,70,70)
2455
+ image_ttf_text(@picture,@font_size,0,tx,ty,c_text_color,@font_name,caption)
2456
+ end
2457
+
2458
+ # Process pie slices
2459
+ if ( !all_black )
2460
+ line_color =allocate_color(@picture,rc,gc,bc)
2461
+ else
2462
+ line_color = allocate_color(@picture,rc,gc,bc)
2463
+ end
2464
+ x_line_last = ""
2465
+ y_line_last = ""
2466
+ i_angle=angle
2467
+ while(i_angle<=angle+value*splice_ratio)
2468
+ pos_x = Math.cos(i_angle * Math::PI / 180 ) * radius + x_pos + x_offset
2469
+ pos_y = Math.sin(i_angle * Math::PI / 180 ) * radius + y_pos + y_offset
2470
+ top_plots[key]<< (pos_x).round
2471
+ top_plots[key] << (pos_y).round
2472
+ if ( i_angle == angle || i_angle == angle+value*splice_ratio || i_angle+0.5 > angle+value*splice_ratio)
2473
+ self.draw_line(x_pos+x_offset,y_pos+y_offset,pos_x,pos_y,rc,gc,bc)
2474
+ end
2475
+ if ( x_line_last != "" )
2476
+ self.draw_line(x_line_last,y_line_last,pos_x,pos_y,rc,gc,bc);
2477
+ end
2478
+ x_line_last = pos_x
2479
+ y_line_last = pos_y
2480
+ i_angle=i_angle+0.5
2481
+ end
2482
+
2483
+ top_plots[key] << (x_pos + x_offset).round
2484
+ top_plots[key]<< (y_pos + y_offset).round
2485
+ angle = i_angle
2486
+ end
2487
+ poly_plots = top_plots
2488
+ # Draw Top polygons
2489
+ poly_plots.each_with_index do |value,key|
2490
+ if ( !all_black )
2491
+ image_filled_polygon(@picture,poly_plots[key],@palette[key]["r"],@palette[key]["g"],@palette[key]["b"]);
2492
+ else
2493
+ image_filled_polygon(@picture,poly_plots[key],@shadow_r_color,@shadow_g_color,@shadow_b_color)
2494
+ end
2495
+ end
2496
+ @shadow_active = shadow_status
2497
+
2498
+ end
2499
+ # This function can be used to set the background color.
2500
+ # The default graph background color is set to white.
2501
+ def draw_background(r,g,b)
2502
+ b,g,r= validate_color(b, g, r)
2503
+ image_filled_rectangle(@picture,0,0,@x_size,@y_size,r,g,b)
2504
+ end
2505
+
2506
+ # You can use this function to fill the background of the picture or of the graph area with a color gradient pattern.
2507
+ # You must specify the starting color with its r,g,b values, the number of shades to apply with the decay parameter and optionnaly the target that can be :
2508
+ # * Rchart:: TARGET_GRAPHAREA The currently defined graph area
2509
+ # * Rchart:: TARGET_BACKGROUND The whole picture background
2510
+ def draw_graph_area_gradient(r,g,b,decay,target=TARGET_GRAPHAREA)
2511
+ b, g, r = validate_color(b, g, r)
2512
+ x1,y1,x2,y2 = 0,0,0,0
2513
+ if ( target == TARGET_GRAPHAREA )
2514
+ x1 = @g_area_x1+1
2515
+ x2 = @g_area_x2-1
2516
+ y1 = @g_area_y1+1
2517
+ y2 = @g_area_y2
2518
+ end
2519
+
2520
+ if ( target == TARGET_BACKGROUND )
2521
+ x1 = 0
2522
+ x2 = @x_size
2523
+ y1 = 0
2524
+ y2 = @y_size
2525
+ end
2526
+ #Positive gradient
2527
+ if ( decay > 0 )
2528
+ y_step = (y2 - y1 - 2) / decay
2529
+ i=0
2530
+ while i<=decay
2531
+ r-=1
2532
+ g-=1
2533
+ b-=1
2534
+ yi1 = y1 + ( i * y_step );
2535
+ yi2 = ( yi1 + ( i * y_step ) + y_step ).ceil
2536
+ yi2 = y2-1 if ( yi2 >= yi2 )
2537
+ image_filled_rectangle(@picture,x1,yi1,x2,yi2,r,g,b)
2538
+ i=i+1
2539
+ end
2540
+ end
2541
+ # Negative gradient
2542
+ if ( decay < 0 )
2543
+ y_step = (y2 - y1 - 2) / -decay
2544
+ yi1 = y1
2545
+ yi2 = y1+y_step
2546
+ i= -decay
2547
+ while i>=0
2548
+ r+=1
2549
+ g+=1
2550
+ b+=1
2551
+ image_filled_rectangle(@picture,x1,yi1,x2,yi2,r,g,b)
2552
+ yi1+= y_step
2553
+ yi2+= y_step
2554
+ yi2 = y2-1 if ( yi2 >= yi2 )
2555
+ i= i-1
2556
+ end
2557
+
2558
+ end
2559
+ end
2560
+
2561
+ # This function draw an aliased rectangle
2562
+ # The upper left and bottom right border positions are used as first 4 arguments.
2563
+ # The last 3 parameters are used to set the border color
2564
+ def draw_rectangle(x1, y1, x2, y2, r, g, b)
2565
+ b, g, r = validate_color(b, g, r)
2566
+ c_rectangle = allocate_color(@picture,r, g, b)
2567
+ x1=x1-0.2
2568
+ y1=y1-0.2
2569
+ x2=x2+0.2
2570
+ y2=y2+0.2
2571
+ self.draw_line(x1,y1,x2,y1,r,g,b)
2572
+ self.draw_line(x2,y1,x2,y2,r,g,b)
2573
+ self.draw_line(x2,y2,x1,y2,r,g,b)
2574
+ self.draw_line(x1,y2,x1,y1,r,g,b)
2575
+ end
2576
+
2577
+ # This function draw an aliased filled rectangle
2578
+ # The upper left and bottom right border positions are used as first 4 arguments. The last R,G,B parameters are used to set the border color.
2579
+ # You can specify if the aliased border will be drawn and the transparency.
2580
+ def draw_filled_rectangle(x1, y1, x2, y2, r, g, b, draw_border=true, alpha=100,no_fall_back=false)
2581
+ x1, x2 = x2, x1 if x2.to_f < x1.to_f
2582
+ y1, y2 = y2, y1 if y2.to_f < y1.to_f
2583
+ b,g,r=validate_color(b, g, r)
2584
+
2585
+ if(alpha == 100)
2586
+ #Process shadows
2587
+ if(@shadow_active && no_fall_back)
2588
+ self.draw_filled_rectangle(x1+@shadow_x_distance,y1+@shadow_y_distance,x2+@shadow_x_distance,y2+@shadow_y_distance,@shadow_r_color,@shadow_g_color,@shadow_b_color,false,@shadow_alpha,true)
2589
+ if(@shadow_blur != 0)
2590
+ alpha_decay = (@shadow_alpha/ @shadow_blur)
2591
+ i =1
2592
+ while i<=@shadow_blur
2593
+ self.draw_filled_rectangle(x1+@shadow_x_distance-i/2,y1+@shadow_y_distance-i/2,x2+@shadow_x_distance-i/2,y2+@shadow_y_distance-i/2,@shadow_r_color,@shadow_g_color,@shadow_b_color,false,@shadow_alpha-alpha_decay*i,true)
2594
+ i = i+1
2595
+ end
2596
+ i = 1
2597
+ while i<=@shadow_blur
2598
+ self.draw_filled_rectangle(x1+@shadow_x_distance+i/2,y1+@shadow_y_distance+i/2,x2+@shadow_x_distance+i/2,y2+@shadow_y_distance+i/2,@shadow_r_color,@shadow_g_color,@shadow_b_color,false,@shadow_alpha-alpha_decay*i,true)
2599
+ i = i+1
2600
+ end
2601
+ end
2602
+ end
2603
+ image_filled_rectangle(@picture,x1.to_f.round,y1.to_f.round,x2.to_f.round,y2.to_f.round,r,g,b)
2604
+ else
2605
+ layer_width = (x2-x1).abs+2
2606
+ layer_height = (y2-y1).abs+2
2607
+ @layers[0] = GD::Image.newTrueColor(layer_width,layer_height)
2608
+ c_white = @layers[0].colorAllocate(255,255,255)
2609
+ image_filled_rectangle(@layers[0],0,0,layer_width,layer_height,255,255,255)
2610
+ @layers[0].transparent(c_white)
2611
+ image_filled_rectangle(@layers[0],1.round,1.round,(layer_width-1).round,(layer_height-1).round,r,g,b)
2612
+ image_copy_merge(@layers[0],@picture,([x1,x2].min-1).round,([y1,y2].min-1).round,0,0,layer_width,layer_height,alpha)
2613
+ #TODO Find out equivalent method
2614
+ @layers[0].destroy
2615
+ end
2616
+ if (draw_border )
2617
+ shadow_settings = @shadow_active
2618
+ @shadow_active = false
2619
+ self.draw_rectangle(x1,y1,x2,y2,r,g,b)
2620
+ @shadow_active = shadow_settings
2621
+ end
2622
+ end
2623
+
2624
+ # This function draw an aliased rectangle with rounded corners
2625
+ # The upper left and bottom right border positions are used as first 4 arguments.
2626
+ # Argument #5 represents the radius of the rounded corner.
2627
+ # The last 3 parameters are used to set the border color.
2628
+ def draw_rounded_rectangle(x1, y1, x2, y2, radius,r, g, b)
2629
+ b, g, r = validate_color(b, g, r)
2630
+
2631
+ c_rectangle = allocate_color(@picture,r,g,b)
2632
+
2633
+ step = 90 / ((3.1418 * radius)/2)
2634
+ i=0
2635
+ while i<=90
2636
+ x = Math.cos((i+180)*3.1418/180) * radius + x1 + radius
2637
+ y = Math.sin((i+180)*3.1418/180) * radius + y1 + radius
2638
+ self.draw_antialias_pixel(x,y,r,g,b)
2639
+
2640
+ x = Math.cos((i-90)*3.1418/180) * radius + x2 - radius
2641
+ y = Math.sin((i-90)*3.1418/180) * radius + y1 + radius
2642
+ self.draw_antialias_pixel(x,y,r,g,b)
2643
+
2644
+ x = Math.cos((i)*3.1418/180) * radius + x2 - radius
2645
+ y = Math.sin((i)*3.1418/180) * radius + y2 - radius
2646
+ self.draw_antialias_pixel(x,y,r,g,b)
2647
+
2648
+ x = Math.cos((i+90)*3.1418/180) * radius + x1 + radius
2649
+ y = Math.sin((i+90)*3.1418/180) * radius + y2 - radius
2650
+ self.draw_antialias_pixel(x,y,r,g,b)
2651
+ i=i+step
2652
+ end
2653
+
2654
+ x1=x1-0.2
2655
+ y1=y1-0.2
2656
+ x2=x2+0.2
2657
+ y2=y2+0.2
2658
+ self.draw_line(x1+radius,y1,x2-radius,y1,r,g,b)
2659
+ self.draw_line(x2,y1+radius,x2,y2-radius,r,g,b)
2660
+ self.draw_line(x2-radius,y2,x1+radius,y2,r,g,b)
2661
+ self.draw_line(x1,y2-radius,x1,y1+radius,r,g,b)
2662
+ end
2663
+ # This function draw an aliased filled rectangle with rounded corners
2664
+ # The upper left and bottom right border positions are used as first 4 arguments.
2665
+ # Argument #5 represents the radius of the rounded corner.
2666
+ # The last 3 parameters are used to set the border color.
2667
+ def draw_filled_rounded_rectangle(x1, y1, x2, y2, radius,r, g, b, draw_border=true, alpha=100)
2668
+ b, g, r = validate_color(b, g, r)
2669
+ c_rectangle = allocate_color(@picture,r,g,b)
2670
+
2671
+ step = 90 / ((3.1418 * radius)/2)
2672
+ i=0
2673
+ while i<=90
2674
+ xi1 = Math.cos((i+180)*3.1418/180) * radius + x1 + radius
2675
+ yi1 = Math.sin((i+180)*3.1418/180) * radius + y1 + radius
2676
+
2677
+ xi2 = Math.cos((i-90)*3.1418/180) * radius + x2 - radius
2678
+ yi2 = Math.sin((i-90)*3.1418/180) * radius + y1 + radius
2679
+
2680
+ xi3 = Math.cos((i)*3.1418/180) * radius + x2 - radius
2681
+ yi3 = Math.sin((i)*3.1418/180) * radius + y2 - radius
2682
+
2683
+ xi4 = Math.cos((i+90)*3.1418/180) * radius + x1 + radius
2684
+ yi4 = Math.sin((i+90)*3.1418/180) * radius + y2 - radius
2685
+
2686
+ image_line(@picture,xi1,yi1,x1+radius,yi1,r,g,b)
2687
+ image_line(@picture,x2-radius,yi2,xi2,yi2,r,g,b)
2688
+ image_line(@picture,x2-radius,yi3,xi3,yi3,r,g,b)
2689
+ image_line(@picture,xi4,yi4,x1+radius,yi4,r,g,b)
2690
+
2691
+ self.draw_antialias_pixel(xi1,yi1,r,g,b)
2692
+ self.draw_antialias_pixel(xi2,yi2,r,g,b)
2693
+ self.draw_antialias_pixel(xi3,yi3,r,g,b)
2694
+ self.draw_antialias_pixel(xi4,yi4,r,g,b)
2695
+ i=i+step
2696
+ end
2697
+ image_filled_rectangle(@picture,x1,y1+radius,x2,y2-radius,r,g,b)
2698
+ image_filled_rectangle(@picture,x1+radius,y1,x2-radius,y2,r,g,b)
2699
+
2700
+ x1=x1-0.2
2701
+ y1=y1-0.2
2702
+ x2=x2+0.2
2703
+ y2=y2+0.2
2704
+ self.draw_line(x1+radius,y1,x2-radius,y1,r,g,b)
2705
+ self.draw_line(x2,y1+radius,x2,y2-radius,r,g,b)
2706
+ self.draw_line(x2-radius,y2,x1+radius,y2,r,g,b)
2707
+ self.draw_line(x1,y2-radius,x1,y1+radius,r,g,b)
2708
+ end
2709
+ # This function draw an aliased circle at position (xc,yc) with the specified radius.
2710
+ # The last 3 parameters are used to set the border color.
2711
+ # Width is used to draw ellipses.
2712
+ def draw_circle(xc,yc,height,r,g,b,width=0)
2713
+ width = height if ( width == 0 )
2714
+ b, g, r = validate_color(b, g, r)
2715
+
2716
+ c_circle = allocate_color(@picture,r,g,b);
2717
+ step = 360 / (2 * 3.1418 * [width,height].max)
2718
+ i =0
2719
+ while(i<=360)
2720
+ x= Math.cos(i*3.1418/180) * height + xc
2721
+ y = Math.sin(i*3.1418/180) * width + yc
2722
+ self.draw_antialias_pixel(x,y,r,g,b)
2723
+ i = i+step
2724
+ end
2725
+ end
2726
+
2727
+ # This function draw a filled aliased circle at position (xc,yc) with the specified radius.
2728
+ # The last 3 parameters are used to set the border and filling color.
2729
+ # Width is used to draw ellipses.
2730
+ def draw_filled_circle(xc,yc,height,r,g,b,width=0)
2731
+ width = height if ( width == 0 )
2732
+ b, g, r = validate_color(b, g, r)
2733
+ step = 360 / (2 * 3.1418 * [width,height].max)
2734
+ i =90
2735
+ while i<=270
2736
+ x1 = Math.cos(i*3.1418/180) * height + xc
2737
+ y1 = Math.sin(i*3.1418/180) * width + yc
2738
+ x2 = Math.cos((180-i)*3.1418/180) * height + xc
2739
+ y2 = Math.sin((180-i)*3.1418/180) * width + yc
2740
+ self.draw_antialias_pixel(x1-1,y1-1,r,g,b)
2741
+ self.draw_antialias_pixel(x2-1,y2-1,r,g,b)
2742
+ image_line(@picture,x1,y1-1,x2-1,y2-1,r,g,b) if ( (y1-1) > yc - [width,height].max )
2743
+ i= i+step
2744
+ end
2745
+ end
2746
+
2747
+ # This function draw an aliased ellipse at position (xc,yc) with the specified height and width.
2748
+ # The last 3 parameters are used to set the border color.
2749
+ def draw_ellipse(xc,yc,height,width,r,g,b)
2750
+ self.draw_circle(xc,yc,height,r,g,b,width)
2751
+ end
2752
+
2753
+
2754
+ # This function draw a filled aliased ellipse at position (xc,yc) with the specified height and width.
2755
+ # The last 3 parameters are used to set the border and filling color.
2756
+ def draw_filled_ellipse(xc,yc,height,width,r,g,b)
2757
+ self.draw_filled_circle(xc,yc,height,r,g,b,width)
2758
+ end
2759
+
2760
+ # This function will draw an aliased line between points (x1,y1) and (x2,y2).
2761
+ # The last 3 parameters are used to set the line color.
2762
+ # The last optional parameter is used for internal calls made by graphing function.If set to true, only portions of line inside the graph area will be drawn.
2763
+
2764
+ def draw_line(x1,y1,x2,y2,r,g,b,graph_function=false)
2765
+ if ( @line_dot_size > 1 )
2766
+ self.draw_dotted_line(x1,y1,x2,y2,@line_dot_size,r,g,b,graph_function)
2767
+ else
2768
+ b, g, r = validate_color(b, g, r)
2769
+ distance = Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)) rescue 0
2770
+ if ( distance == 0 )
2771
+ return -1
2772
+ else
2773
+ x_step = (x2-x1) / distance
2774
+ y_step = (y2-y1) / distance
2775
+ i =0
2776
+ while i<=distance
2777
+ x = i * x_step + x1
2778
+ y = i * y_step + y1
2779
+ if((x >= @g_area_x1.to_f && x <= @g_area_x2.to_f && y >= @g_area_y1.to_f && y <= @g_area_y2.to_f) || !graph_function )
2780
+ if ( @line_width == 1 )
2781
+ self.draw_antialias_pixel(x,y,r,g,b)
2782
+ else
2783
+ start_offset = -(@line_width/2)
2784
+ end_offset = (@line_width/2)
2785
+ j = start_offset
2786
+
2787
+ while j<=end_offset
2788
+ self.draw_antialias_pixel(x+j,y+j,r,g,b)
2789
+ j+=1
2790
+ end
2791
+ end
2792
+ end
2793
+ i =i+1
2794
+ end
2795
+ end
2796
+ end
2797
+ end
2798
+
2799
+ # This function will draw an aliased dotted line between points (x1,y1) and (x2,y2).
2800
+ # Parameter #5 is used to specify the dot size ( 2 will draw 1 point every 2 points )
2801
+ # The last 3 parameters are used to set the line color.
2802
+ def draw_dotted_line(x1,y1,x2,y2,dot_size,r,g,b,graph_function=false)
2803
+ b, g, r = validate_color(b, g, r)
2804
+ distance = Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
2805
+ x_step = (x2-x1) / distance
2806
+ y_step = (y2-y1) / distance
2807
+ dot_index = 0
2808
+ i = 0
2809
+ start_offset = 0
2810
+
2811
+ while(i<=distance)
2812
+ x = i * x_step + x1
2813
+ y = i * y_step + y1
2814
+ if ( dot_index <= dot_size)
2815
+ if ( (x >= @g_area_x1 && x <= @g_area_x2 && y >= @g_area_y1 && y <= @g_area_y2) || !graph_function )
2816
+ if (@line_width == 1 )
2817
+ self.draw_antialias_pixel(x,y,r,g,b)
2818
+ else
2819
+ start_offset = start_offset -(@line_width/2)
2820
+ end_offset = (@line_width/2)
2821
+ j = start_offset
2822
+ while(j<= end_offset)
2823
+ self.draw_antialias_pixel(x+j,y+j,r,g,b)
2824
+ j= j+1
2825
+ end
2826
+ end
2827
+ end
2828
+ end
2829
+ dot_index = dot_index+1
2830
+ dot_index = 0 if (dot_index == dot_size * 2)
2831
+ i= i+1
2832
+ end
2833
+
2834
+ end
2835
+
2836
+ # This function allows you to merge an external PNG picture with your graph specifying the position and the transparency
2837
+ def draw_from_png(file_name,x,y,alpha=100)
2838
+ self.draw_from_picture(1,file_name,x,y,alpha)
2839
+ end
2840
+
2841
+ #This function allows you to merge an external GIF picture with your graph specifying the position and the transparenc
2842
+ #def draw_from_gif(file_name,x,y,alpha=100)
2843
+ #self.draw_from_picture(2,file_name,x,y,alpha)
2844
+ #end
2845
+
2846
+ # This function allows you to merge an external JPG picture with your graph specifying the position and the transparency.
2847
+ def draw_from_jpg(file_name,x,y,alpha=100)
2848
+ self.draw_from_picture(3,file_name,x,y,alpha)
2849
+ end
2850
+
2851
+ # Generic loader function for external pictures accepts png format
2852
+ def draw_from_picture(pic_type,file_name,x,y,alpha=100)
2853
+ if ( File.exist?(file_name))
2854
+ raster = image_create_from_png(file_name) if ( pic_type == 1 )
2855
+ # raster = image_create_from_gif(file_name) if ( pic_type == 2 )
2856
+ raster = image_create_from_jpeg(file_name) if ( pic_type == 3 )
2857
+ infos = get_image_size(raster)
2858
+ width = infos[0]
2859
+ height = infos[1]
2860
+ image_copy_merge(raster,@picture,x,y,0,0,width,height,alpha)
2861
+ image_destroy(raster)
2862
+ end
2863
+ end
2864
+
2865
+ # This function will draw an alpha pixel at position (x,y).
2866
+ # alpha is used to specify the transparency factor ( between 0 and 100 )
2867
+ # The last 3 parameters are used to set the pixel color.
2868
+ def draw_alpha_pixel(x,y,alpha,r,g,b)
2869
+ b, g, r = validate_color(b, g, r)
2870
+ if ( x < 0 || y < 0 || x >= @x_size || y >= @y_size )
2871
+ #eturn(-1)
2872
+ #TODO check image_color_at method is right?
2873
+ else
2874
+ rgb2= image_color_at(@picture, x, y)
2875
+
2876
+ r2 = (rgb2 >> 16) & 0xFF
2877
+ g2 = (rgb2 >> 8) & 0xFF
2878
+ b2 = rgb2 & 0xFF
2879
+ i_alpha = (100 - alpha)/100
2880
+ alpha = alpha / 100
2881
+ ra = (r*alpha+r2*i_alpha).floor
2882
+ ga = (g*alpha+g2*i_alpha).floor
2883
+ ba = (b*alpha+b2*i_alpha).floor
2884
+ image_set_pixel(@picture,x,y,ra,ga,ba)
2885
+ end
2886
+ end
2887
+ # color helper
2888
+ def allocate_color(picture,r,g,b,factor=0)
2889
+ r = r + factor
2890
+ g = g + factor
2891
+ b = b + factor
2892
+ r = 0 if ( r < 0 )
2893
+ r = 255 if ( r > 255 )
2894
+ g = 0 if ( g < 0 )
2895
+ g = 255 if ( g > 255 )
2896
+ b = 0 if ( b < 0 )
2897
+ b = 255 if ( b > 255 )
2898
+ image_color_allocate(picture,r,g,b)
2899
+ end
2900
+
2901
+ # Use this function to add a border to your picture. Be carefull, drawing a border will change all the chart components positions, thus this call must be the last one before one of the rendering methods!!!
2902
+ # You can specify the size of the border and its color.
2903
+ # The width and height of the picture will be modified by 2x the size value.
2904
+ def add_border(size=3,r=0,g=0,b=0)
2905
+ width = @x_size+2*size
2906
+ height = @y_size+2*size
2907
+ resampled = image_create_true_color(width,height);
2908
+ image_filled_rectangle(resampled,0,0,width,height, r, g, b)
2909
+ image_copy(@picture,resampled,size,size,0,0,@x_size,@y_size)
2910
+ image_destroy(@picture)
2911
+ @x_size = width;
2912
+ @y_size = height;
2913
+ @picture = image_create_true_color(@x_size,@y_size);
2914
+ image_filled_rectangle(@picture,0,0,@x_size,@y_size,255,255,255)
2915
+ image_color_transparent(@picture,255,255,255)
2916
+ image_copy(resampled,@picture,0,0,0,0,@x_size,@y_size)
2917
+ end
2918
+
2919
+ # Private functions for internal processing Internal function.
2920
+ def draw_antialias_pixel(x,y,r,g,b,alpha=100,no_fall_back=false)
2921
+ #Process shadows
2922
+ if(@shadow_active && !no_fall_back)
2923
+ self.draw_antialias_pixel(x+@shadow_x_distance,y+@shadow_y_distance,@shadow_r_color,@shadow_g_color,@shadow_b_color,@shadow_alpha,true)
2924
+ if(@shadow_blur != 0)
2925
+ alpha_decay = (@shadow_alpha*1.0 / @shadow_blur)
2926
+ i=1
2927
+ while i<=@shadow_blur
2928
+ self.draw_antialias_pixel(x+@shadow_x_distance-i/2,y+@shadow_y_distance-i/2,@shadow_r_color,@shadow_g_color,@shadow_b_color,@shadow_alpha-alpha_decay*i,true)
2929
+ i = i+1
2930
+ end
2931
+ i =1
2932
+ while i<=@shadow_blur
2933
+ self.draw_antialias_pixel(x+@shadow_x_distance+i/2,y+@shadow_y_distance+i/2,@shadow_r_color,@shadow_g_color,@shadow_b_color,@shadow_alpha-alpha_decay*i,true)
2934
+ i = i+1
2935
+ end
2936
+ end
2937
+ end
2938
+ b, g, r = validate_color(b, g, r)
2939
+ plot = ""
2940
+ xi = x.floor rescue 0
2941
+ yi = y.floor rescue 0
2942
+ if ( xi == x && yi == y)
2943
+ if ( alpha == 100 )
2944
+ image_set_pixel(@picture,x,y,r,g,b)
2945
+ else
2946
+ self.draw_alpha_pixel(x,y,alpha,r,g,b)
2947
+ end
2948
+ else
2949
+ if xi > 0 || yi > 0 #soe error occures therefor added condtion
2950
+ alpha1 = (((1 - (x - x.floor)) * (1 - (y - y.floor)) * 100) / 100) * alpha
2951
+ self.draw_alpha_pixel(xi,yi,alpha1,r,g,b) if alpha1 > @anti_alias_quality
2952
+ alpha2 = (((x - x.floor) * (1 - (y - y.floor)) * 100) / 100) * alpha
2953
+ self.draw_alpha_pixel(xi+1,yi,alpha2,r,g,b) if alpha2 > @anti_alias_quality
2954
+ alpha3 = (((1 - (x - x.floor)) * (y - y.floor) * 100) / 100) * alpha
2955
+ self.draw_alpha_pixel(xi,yi+1,alpha3,r,g,b) if alpha3 > @anti_alias_quality
2956
+ alpha4 = (((x - x.floor) * (y - y.floor) * 100) / 100) * alpha
2957
+ self.draw_alpha_pixel(xi+1,yi+1,alpha4,r,g,b) if alpha4 > @anti_alias_quality
2958
+ end
2959
+ end
2960
+ end
2961
+
2962
+ # Validate data contained in the description array Internal function
2963
+ def validate_data_description(function_name,data_description,description_required=true)
2964
+ if (data_description["position"].nil?)
2965
+ @errors << "[Warning] #{function_name} - Y Labels are not set."
2966
+ data_description["position"] = "name";
2967
+ end
2968
+
2969
+ if (description_required)
2970
+ if ((data_description["description"].nil?))
2971
+ @errors << "[Warning] #{function_name} - Series descriptions are not set."
2972
+ data_description["values"].each do |value|
2973
+ if data_description["description"].nil?
2974
+ data_description["description"]={value=> value}
2975
+ else
2976
+ data_description["description"]=data_description["description"].merge(value=>value)
2977
+ end
2978
+ end
2979
+ end
2980
+
2981
+ data_desc_count = data_description["values"].is_a?(Array) ? data_description["values"].count : 1
2982
+ if ((data_description["description"].count) < data_desc_count)
2983
+ @errors << "[Warning] #{function_name} - Some series descriptions are not set."
2984
+ data_description["values"].each do |value|
2985
+ data_description["description"][value] = value if ( data_description["description"][value].nil?)
2986
+ end
2987
+ end
2988
+ end
2989
+ return data_description
2990
+ end
2991
+
2992
+ #Validate data contained in the data array Internal function
2993
+ def validate_data(function_name,data)
2994
+ data_summary = {}
2995
+ data.each do |v|
2996
+ v.each do |key,val|
2997
+
2998
+ if (data_summary[key].nil?)
2999
+ data_summary[key] = 1
3000
+ else
3001
+ data_summary[key] = data_summary[key]+1
3002
+ end
3003
+ end
3004
+ end
3005
+ if ( data_summary.max.last == 0 ) #TODO Check method
3006
+ @errors << "[Warning] #{function_name} No data set."
3007
+ end
3008
+ data_summary.each do |k,v|
3009
+ if v < data_summary.max.last
3010
+ @errors << "#{function_name} Missing Data in serie #{key}"
3011
+ end
3012
+ end
3013
+ return data
3014
+ end
3015
+ # Activate the image map creation process Internal function
3016
+ def set_image_map(mode=true,graph_id="MyGraph")
3017
+ @build_map = mode
3018
+ @map_id = graph_id
3019
+ end
3020
+ # Add a box into the image map Internal function
3021
+ def add_to_image_map(x1,y1,x2,y2,serie_name,value,caller_function)
3022
+ if ( @map_function == nil || @map_function == caller_function )
3023
+ @image_map << (x1.round).to_s+","+(y1.round).to_s+","+(x2.round).to_s+","+(y2.round).to_s+","+serie_name+","+value.to_s
3024
+ @map_function = caller_function
3025
+ end
3026
+ end
3027
+
3028
+ #Convert seconds to a time format string
3029
+ def to_time(value)
3030
+ hour = (value/3600).floor
3031
+ minute = ((value - hour*3600)/60).floor
3032
+ second =(value - hour*3600 - minute*60).floor
3033
+
3034
+ hour = "0.#{Hour}" if (hour.length == 1 )
3035
+ minute = "0.#{minute}" if (minute.length == 1 )
3036
+ second = "0.#{second}" if (second.length == 1 )
3037
+
3038
+ return ("#{hour}.:.#{minute}}.:.#{second}")
3039
+ end
3040
+
3041
+ # Convert to metric system */
3042
+ def to_metric(value)
3043
+ go = (value/1000000000).floor
3044
+ mo = ((value - go*1000000000)/1000000).floor
3045
+ ko = ((value - go*1000000000 - mo*1000000)/1000).floor
3046
+ o = (value - go*1000000000 - mo*1000000 - ko*1000).floor
3047
+
3048
+ return("#{go}..#{mo}.g") if (go != 0)
3049
+ return("#{mo}...#{ko}.m") if (mo != 0)
3050
+ return("#{ko}...#{o}).k") if (ko != 0)
3051
+ return(o)
3052
+ end
3053
+
3054
+ # Convert to curency
3055
+ def to_currency(value)
3056
+ go = (value/1000000000).floor
3057
+ mo = ((value - go*1000000000)/1000000).floor
3058
+ ko = ((value - go*1000000000 - mo*1000000)/1000).floor
3059
+ o = (value - go*1000000000 - mo*1000000 - ko*1000).floor
3060
+
3061
+ o = "00.#{o}" if ( (o.length) == 1 )
3062
+ o = "0.#{o}" if ( (o.length) == 2 )
3063
+
3064
+ result_string = o
3065
+ result_string = "#{ko}...#{result_string}" if ( ko != 0 )
3066
+ result_string = "#{mo}...#{result_string}" if ( mo != 0 )
3067
+ result_string = "#{go}...#{result_string}" if ( go != 0 )
3068
+
3069
+ result_string = @currency.result_strin
3070
+ return(result_string)
3071
+ end
3072
+ # Set date format for axis labels TODO
3073
+ def set_date_format(format)
3074
+ @date_format = format
3075
+ end
3076
+
3077
+ def to_date(value)
3078
+ #return(Time.parse(value))
3079
+ end
3080
+ # Check if a number is a full integer (for scaling)
3081
+ def is_real_int(value)
3082
+ value.ceil == value.floor
3083
+ end
3084
+ # round of particular decimal
3085
+ def round_of(no,n=0)
3086
+ (no * (10.0 ** n)).round * (10.0 ** (-n))
3087
+ end
3088
+
3089
+ #convert degree to radian
3090
+ def deg2rad(deg)
3091
+ deg*Math::PI/180
3092
+ end
3093
+
3094
+ def raise_fatal(message)
3095
+ puts "[FATAL] "+message
3096
+ return -1
3097
+ end
3098
+ # Print all error messages on the CLI or graphically
3099
+ def print_errors(mode="cli")
3100
+ return(0) if (@errors.count == 0)
3101
+
3102
+ if mode == "cli"
3103
+ @errors.each do |value|
3104
+ puts value
3105
+ end
3106
+ elsif ( mode == "gd" )
3107
+ self.set_line_style(width=1)
3108
+ max_width = 0
3109
+ @errors.each do |value|
3110
+ position = image_ftb_box(@error_font_size,0,@error_font_name,value)
3111
+ text_width = position[2]-position[0]
3112
+ max_width = text_width if ( text_width > max_width )
3113
+ end
3114
+ self.draw_filled_rounded_rectangle(@x_size-(max_width+20),@y_size-(20+((@error_font_size+4)*(@errors.count))),@x_size-10,@y_size-10,6,233,185,185)
3115
+ self.draw_rounded_rectangle(@x_size-(max_width+20),@y_size-(20+((@error_font_size+4)*(@errors.count))),@x_size-10,@y_size-10,6,193,145,145)
3116
+ c_text_color = allocate_color(@picture,133,85,85)
3117
+ ypos = @y_size - (18 + ((@errors.count)-1) * (@error_font_size + 4))
3118
+ @errors.each do |value|
3119
+ image_ttf_text(@picture,@error_font_size,0,@x_size-(max_width+15),ypos,c_text_color,@error_font_name,value)
3120
+ ypos = ypos + (@error_font_size + 4);
3121
+ end
3122
+ end
3123
+ end
3124
+ # render Graph as png format
3125
+ def render_png(file_name)
3126
+ self.print_errors(@error_interface) if ( @error_reporting )
3127
+ file = File.new(file_name,"wb")
3128
+ @picture.png(file)
3129
+ file.close
3130
+ end
3131
+ # render Graph as jpeg format
3132
+ def render_jpeg(file_name,quality=0)
3133
+ self.print_errors(@error_interface) if ( @error_reporting )
3134
+ file = File.new(file_name,"wb")
3135
+ @picture.jpeg(file,quality)
3136
+ file.close
3137
+ end
3138
+ # resize image on passing png,jpeg,or gd image
3139
+ # pass file_name/gd image,new_file_name,percentage,or resize width,resize height
3140
+ def resize_image(file_name,resize_file_name="test",percentage=0,resized_width=0,resized_height=0)
3141
+ image = GD::Image.new_from_png(file_name) rescue ""
3142
+ render_file_as = "png"
3143
+ if !image.is_a?(GD::Image)
3144
+ image = GD::Image.new_from_jpeg(file_name) rescue ""
3145
+ render_file_as = "jpeg"
3146
+ elsif !image.is_a?(GD::Image)
3147
+ image = GD::Image.new_from_gd(file_name) rescue ""
3148
+ render_file_as = "png"
3149
+ end
3150
+
3151
+ if image.is_a?(GD::Image)
3152
+ width=image.width
3153
+ height=image.height
3154
+ if percentage >0
3155
+ resized_width = (width*percentage)/100.0
3156
+ resized_height = (height*percentage)/100.0
3157
+ elsif(resized_width != 0 && resized_height ==0)
3158
+
3159
+ resized_height = (100 /(width*1.0/resized_width) ) * 0.01
3160
+ resized_height = (height * resized_height).round
3161
+ elsif( resized_height != 0 && resized_width ==0)
3162
+
3163
+ resized_width = (100 /(height*1.0/resized_height) ) * 0.01
3164
+ resized_width = (width * resized_width).round
3165
+ else
3166
+ resized_width = 100
3167
+ resized_height = 100
3168
+ end
3169
+
3170
+ resize_image = GD::Image.newTrueColor(resized_width, resized_height)
3171
+ image.copyResized(resize_image, 0,0,0,0, resized_width,resized_height, width, height)
3172
+ file=File.new(resize_file_name,"wb")
3173
+ if render_file_as == "png"
3174
+ resize_image.png(file)
3175
+ elsif render_file_as == "jpeg"
3176
+ resize_image.jpeg(file)
3177
+ end
3178
+ file.close
3179
+ else
3180
+ puts "Provide proper image"
3181
+ end
3182
+ end
3183
+ ##########################################3
3184
+ #GD MAP FUNCTION HELPER
3185
+ #ON NEXT VERSION TRY TO MAP THIS FUNCTION WITH GD2 Gem
3186
+ def image_ttf_text(picture,font_size,angle,x_pos,y_pos,fg_color,font_name,str)
3187
+ angle = deg2rad(angle)
3188
+ err,brect=picture.stringTTF(fg_color, font_name, font_size, angle, x_pos, y_pos, str.to_s)
3189
+ end
3190
+
3191
+ def image_ftb_box(font_size,angle,font_name,str,x=0,y=0)
3192
+ angle = deg2rad(angle)
3193
+ err,brect = GD::Image.stringFT(0, font_name, font_size, angle, x, y, str)
3194
+ brect
3195
+ end #Compute and draw the scale
3196
+
3197
+ def image_color_allocate(picture,r,g,b)
3198
+ picture.colorAllocate(r,g,b)
3199
+ end
3200
+
3201
+ def image_set_pixel(picture,x,y,r,g,b)
3202
+ color=image_color_allocate(picture,r,g,b)
3203
+ picture.setPixel(x,y,color)
3204
+ end
3205
+
3206
+ def image_color_at(picture,x,y)
3207
+ color = picture.getPixel(x, y)
3208
+ end
3209
+
3210
+ def image_line(picture,x1,y1,x2,y2,r,g,b)
3211
+ picture.line(x1, y1, x2, y2, allocate_color(picture,r,g,b))
3212
+ end
3213
+
3214
+ def image_filled_rectangle(picture,x1,y1,x2,y2,r,g,b)
3215
+ color = picture.colorAllocate(r,g,b)
3216
+ picture.filledRectangle(x1, y1, x2, y2, color)
3217
+ end
3218
+
3219
+ def image_create_true_color(width,height)
3220
+ GD::Image.newTrueColor(width, height)
3221
+ end
3222
+
3223
+ def image_copy_merge(src_pic,dest_pic, dst_x, dst_y, src_x, src_y, w, h, pct, gray = false)
3224
+ src_pic.copyMerge(dest_pic, dst_x, dst_y, src_x, src_y, w, h, pct)
3225
+ end
3226
+
3227
+ def image_copy(src_pic,dst_pic,dest_x, dest_y, self_x, self_y, width, height)
3228
+ src_pic.copy(dst_pic,dest_x, dest_y, self_x, self_y, width, height)
3229
+ end
3230
+
3231
+ def image_color_transparent(im,r,g,b)
3232
+ color=allocate_color(im, r, g, b)
3233
+ im.transparent(color)
3234
+ end
3235
+
3236
+ def image_destroy(image)
3237
+ image.destroy
3238
+ end
3239
+
3240
+ def image_create_from_png(file_name)
3241
+ GD::Image.new_from_png(file_name)
3242
+ end
3243
+ def image_create_from_jpeg(file_name)
3244
+ GD::Image.new_from_jpeg(file_name)
3245
+ end
3246
+
3247
+ def get_image_size(image)
3248
+ [image.width,image.height]
3249
+ end
3250
+
3251
+ def image_filled_polygon(picture,points,r,g,b,points_count=0)
3252
+ color = allocate_color(picture,r,g,b)
3253
+ polygon=GD::Polygon.new
3254
+ i=0
3255
+ if points_count == 0
3256
+ num_points = (points.length+1)
3257
+ else
3258
+ num_points = points_count+points_count
3259
+ end
3260
+ while(i<=num_points)
3261
+ j =i
3262
+ polygon.addPt(points[j],points[j+1]) if(!points[j+1].nil?)
3263
+ i = i+2
3264
+ end
3265
+ picture.filledPolygon(polygon, color)
3266
+ end
3267
+
3268
+ end
3269
+
3270
+