gerbilcharts 0.2.13 → 0.5.9
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +35 -0
- data/Manifest.txt +8 -0
- data/README.txt +8 -8
- data/lib/gerbilcharts/charts.rb +3 -0
- data/lib/gerbilcharts/charts/bubble_chart.rb +30 -0
- data/lib/gerbilcharts/charts/conversation_ring.rb +31 -0
- data/lib/gerbilcharts/charts/line_chart.rb +1 -0
- data/lib/gerbilcharts/charts/matrix_chart.rb +32 -0
- data/lib/gerbilcharts/charts/square_line_chart.rb +7 -0
- data/lib/gerbilcharts/models.rb +1 -0
- data/lib/gerbilcharts/models/bucketized_timeseries_graph_model.rb +1 -1
- data/lib/gerbilcharts/models/graph_model_group.rb +19 -2
- data/lib/gerbilcharts/models/matrix_model.rb +85 -0
- data/lib/gerbilcharts/models/monotonous_graph_model.rb +5 -1
- data/lib/gerbilcharts/models/presets.rb +18 -11
- data/lib/gerbilcharts/models/raw_range.rb +1 -1
- data/lib/gerbilcharts/models/round_range.rb +3 -1
- data/lib/gerbilcharts/models/round_time_range.rb +19 -29
- data/lib/gerbilcharts/models/sampled_timeseries_graph_model.rb +1 -1
- data/lib/gerbilcharts/public/brushmetal.css +123 -74
- data/lib/gerbilcharts/public/gerbil.js +104 -64
- data/lib/gerbilcharts/surfaces.rb +3 -0
- data/lib/gerbilcharts/surfaces/bubble_surface.rb +105 -0
- data/lib/gerbilcharts/surfaces/chart.rb +4 -2
- data/lib/gerbilcharts/surfaces/conversation_ring.rb +64 -0
- data/lib/gerbilcharts/surfaces/detailed_legend.rb +35 -13
- data/lib/gerbilcharts/surfaces/graph_element.rb +8 -1
- data/lib/gerbilcharts/surfaces/grid.rb +8 -2
- data/lib/gerbilcharts/surfaces/horizontal_axis.rb +12 -3
- data/lib/gerbilcharts/surfaces/horizontal_time_axis.rb +5 -4
- data/lib/gerbilcharts/surfaces/impulse_surface.rb +1 -1
- data/lib/gerbilcharts/surfaces/legend.rb +43 -6
- data/lib/gerbilcharts/surfaces/mark_band.rb +17 -1
- data/lib/gerbilcharts/surfaces/matrix_surface.rb +87 -0
- data/lib/gerbilcharts/surfaces/pie_surface.rb +109 -96
- data/lib/gerbilcharts/surfaces/rect.rb +18 -0
- data/lib/gerbilcharts/surfaces/stacked_area_surface.rb +42 -2
- data/lib/gerbilcharts/surfaces/stacked_grid.rb +1 -3
- data/lib/gerbilcharts/surfaces/title_panel.rb +7 -2
- data/lib/gerbilcharts/surfaces/tracker.rb +4 -1
- data/lib/gerbilcharts/surfaces/vertical_axis.rb +2 -2
- data/lib/gerbilcharts/svgdc.rb +1 -0
- data/lib/gerbilcharts/svgdc/css_inliner.rb +2 -2
- data/lib/gerbilcharts/svgdc/svg_ellipse.rb +21 -0
- data/lib/gerbilcharts/svgdc/svg_polygon.rb +19 -0
- data/lib/gerbilcharts/svgdc/svgdc.rb +4 -1
- data/lib/gerbilcharts/version.rb +2 -3
- data/test/test_bar.rb +1 -1
- data/test/test_bubble.rb +52 -0
- data/test/test_conversation.rb +34 -0
- data/test/test_lines.rb +3 -3
- data/test/test_matrix.rb +34 -0
- data/test/test_noob.rb +9 -6
- data/test/test_pie.rb +2 -2
- data/test/test_ranges.rb +15 -2
- data/test/test_sa.rb +88 -0
- metadata +14 -2
@@ -4,6 +4,9 @@ module GerbilCharts::Surfaces
|
|
4
4
|
# legend panel shows names/colors of items in a separate box (transparent)
|
5
5
|
class DetailedLegend < GraphElement
|
6
6
|
|
7
|
+
WIDTH_MAX = 500
|
8
|
+
STAT_TABLE_OFFSET=120
|
9
|
+
|
7
10
|
attr_reader :width
|
8
11
|
attr_reader :showvalues
|
9
12
|
|
@@ -15,16 +18,34 @@ class DetailedLegend < GraphElement
|
|
15
18
|
|
16
19
|
def int_render(g)
|
17
20
|
|
21
|
+
return unless parent.get_global_option(:enable_detailed_legend,true)
|
22
|
+
|
18
23
|
w=g.newwin("legendpanel_detail", {:visibility => 'hidden'} )
|
19
24
|
g.setactivewindow(w)
|
20
25
|
|
26
|
+
|
27
|
+
# get max modname, used to calculate width of detailed panel
|
28
|
+
max_name_length=0
|
29
|
+
parent.modelgroup.each_model do | mod |
|
30
|
+
max_name_length = [max_name_length,mod.name.length].max
|
31
|
+
end
|
32
|
+
|
33
|
+
@bounds.left = @bounds.right-WIDTH_MAX if @bounds.width > WIDTH_MAX
|
34
|
+
|
21
35
|
# count determines the bounds
|
22
36
|
@bounds.bottom = @bounds.top + 16 + 15 * parent.modelgroup.count
|
23
37
|
g.rectangle_r(@bounds, {:class => @class})
|
24
38
|
|
25
39
|
# toggle detail/mini legend
|
26
40
|
g.rectangle(@bounds.left-5,@bounds.top,5,5, {:href => "javascript:void(0);",
|
27
|
-
:onclick => "showMiniLegend();"
|
41
|
+
:onclick => "showMiniLegend();",
|
42
|
+
:fill => "white",
|
43
|
+
:stroke => "none",
|
44
|
+
:gerbiltooltip1 => "Click to show compact legend" })
|
45
|
+
g.textout(@bounds.left-4,@bounds.top+5,'>', {'font-size' => '9', :href => "javascript:void(0);",
|
46
|
+
:onclick => "showMiniLegend();", :stroke => '#DDDDDD',
|
47
|
+
:gerbiltooltip1 => "Click to show compact legend" })
|
48
|
+
|
28
49
|
|
29
50
|
rbox = Rect.new
|
30
51
|
rbox.initfrom(@bounds)
|
@@ -34,12 +55,10 @@ class DetailedLegend < GraphElement
|
|
34
55
|
rbox.bottom = rbox.top+10
|
35
56
|
|
36
57
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
stat_label_pos += stat_label_width
|
42
|
-
end
|
58
|
+
|
59
|
+
stat_label_pos = rbox.right + STAT_TABLE_OFFSET
|
60
|
+
lab = %w(Max Min Avg Latest).inject("") { |m,ai| m += ai.rjust(9)}
|
61
|
+
g.textout(stat_label_pos, rbox.bottom-2, lab, {'xml:space' => 'preserve', :class => "legendstats"} )
|
43
62
|
|
44
63
|
rbox.top += 16
|
45
64
|
rbox.bottom = rbox.top+10
|
@@ -52,16 +71,19 @@ class DetailedLegend < GraphElement
|
|
52
71
|
opts.store(:gerbiltooltip1, mod.name)
|
53
72
|
opts.store(:gerbiltooltip2, "Latest Value = #{mod.latest_val}")
|
54
73
|
|
55
|
-
|
74
|
+
boxopts = { :id => "item#{i}" }
|
75
|
+
boxopts.store(:fill , color_for_id(i)) if i > 10
|
76
|
+
g.rectangle_r(rbox, boxopts )
|
56
77
|
g.textout(rbox.right+5, rbox.bottom-2, mod.name,opts)
|
57
78
|
|
58
79
|
stat_ana = mod.get_statistical_analysis
|
59
80
|
|
60
|
-
stat_label_pos = rbox.right +
|
61
|
-
[
|
62
|
-
|
63
|
-
|
64
|
-
|
81
|
+
stat_label_pos = rbox.right + STAT_TABLE_OFFSET
|
82
|
+
outs = mod.formatted_val(stat_ana[1]).to_s.rjust(9) +
|
83
|
+
mod.formatted_val(stat_ana[0]).to_s.rjust(9) +
|
84
|
+
mod.formatted_val(stat_ana[2]).to_s.rjust(9) +
|
85
|
+
mod.formatted_val(stat_ana[4]).to_s.rjust(9)
|
86
|
+
g.textout(stat_label_pos, rbox.bottom-2, outs, {'xml:space' => 'preserve', :class => 'legendstats'} )
|
65
87
|
|
66
88
|
|
67
89
|
rbox.top += 10 + 5
|
@@ -69,6 +69,7 @@ class GraphElement
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def scale_y val,range
|
72
|
+
return 0 if range.invalid?
|
72
73
|
return @bounds.bottom - @bounds.height * range.scale_factor(val)
|
73
74
|
end
|
74
75
|
|
@@ -143,7 +144,7 @@ class GraphElement
|
|
143
144
|
|
144
145
|
# query a global option, return the defval if option is not set
|
145
146
|
def get_global_option(optsym, defval)
|
146
|
-
@global_chart_options[optsym]
|
147
|
+
@global_chart_options.has_key?(optsym)? @global_chart_options[optsym]: defval
|
147
148
|
end
|
148
149
|
|
149
150
|
# utility methods to derived classes
|
@@ -157,6 +158,12 @@ protected
|
|
157
158
|
return (a<b)?a:b
|
158
159
|
end
|
159
160
|
|
161
|
+
# random but consistent color for ID
|
162
|
+
# depends on a lazily generated map of 100 colors
|
163
|
+
def color_for_id(id)
|
164
|
+
@@mcolors ||= Array.new(100){ |t| "#%06x" % (rand * 0xffffff) }
|
165
|
+
return @@mcolors[id]
|
166
|
+
end
|
160
167
|
|
161
168
|
end
|
162
169
|
|
@@ -32,10 +32,16 @@ class Grid < GraphElement
|
|
32
32
|
end
|
33
33
|
|
34
34
|
|
35
|
+
scaling_x = parent.get_global_option(:scaling_x,:auto)
|
36
|
+
rwx = parent.modelgroup.effective_range_x(scaling_x)
|
37
|
+
if scaling_x.is_a? Array
|
38
|
+
rx = GerbilCharts::Models::RoundTimeRange.new(rwx)
|
39
|
+
end
|
40
|
+
|
35
41
|
# vertical subticks
|
36
42
|
g.newwin('vgrid', {:class => "gridlinev"} ) do |g|
|
37
43
|
rx.each_label do |val,label|
|
38
|
-
xp = scale_x val,
|
44
|
+
xp = scale_x val,rwx
|
39
45
|
g.line(xp,@bounds.top,xp,@bounds.bottom)
|
40
46
|
end
|
41
47
|
end
|
@@ -43,7 +49,7 @@ class Grid < GraphElement
|
|
43
49
|
|
44
50
|
g.newwin('vgridsub', {:class => "gridlinesub"} ) do |g|
|
45
51
|
rx.each_tick(0) do |val|
|
46
|
-
xp = scale_x val,
|
52
|
+
xp = scale_x val,rwx
|
47
53
|
g.line(xp,@bounds.top,xp,@bounds.bottom)
|
48
54
|
end
|
49
55
|
end
|
@@ -15,8 +15,15 @@ class HorizontalAxis < Axis
|
|
15
15
|
range_options_x = parent.get_global_option(:scaling_x,:auto)
|
16
16
|
rawx = parent.modelgroup.effective_range_x(range_options_x)
|
17
17
|
|
18
|
+
|
18
19
|
# need roundx for labels
|
19
|
-
|
20
|
+
if range_options_x.is_a? Array
|
21
|
+
roundx = GerbilCharts::Models::RoundTimeRange.new rawx
|
22
|
+
else
|
23
|
+
roundx = parent.modelgroup.effective_round_range_x
|
24
|
+
end
|
25
|
+
|
26
|
+
|
20
27
|
roundx.each_label do |val,label|
|
21
28
|
xp = scale_x val,rawx
|
22
29
|
|
@@ -27,8 +34,10 @@ class HorizontalAxis < Axis
|
|
27
34
|
xp = @bounds.right-10
|
28
35
|
end
|
29
36
|
|
30
|
-
|
31
|
-
|
37
|
+
if (xp>=15)
|
38
|
+
g.textout(xp, @bounds.top+11, label, {:class => "axislabel", "text-anchor" => "middle"})
|
39
|
+
g.line(xp,@bounds.top-2,xp,@bounds.top+1, {:class => "axistickmajor"})
|
40
|
+
end
|
32
41
|
end
|
33
42
|
|
34
43
|
end
|
@@ -13,12 +13,13 @@ class HorizontalTimeAxis < HorizontalAxis
|
|
13
13
|
def int_render(g)
|
14
14
|
super
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
range_options_x = parent.get_global_option(:scaling_x,:auto)
|
17
|
+
rx = parent.modelgroup.effective_range_x(range_options_x)
|
18
|
+
sfmt = Time.at(rx.rmin).getlocal.to_s
|
18
19
|
|
19
20
|
|
20
|
-
g.textout(@bounds.left-20, @bounds.top+
|
21
|
-
g.line(@bounds.left,@bounds.top,@bounds.left,@bounds.top+
|
21
|
+
g.textout(@bounds.left-20, @bounds.top+22, sfmt, {:class => "axislabelt0" })
|
22
|
+
g.line(@bounds.left,@bounds.top,@bounds.left,@bounds.top+2,{:class => "axistickmajor"})
|
22
23
|
|
23
24
|
end
|
24
25
|
|
@@ -14,6 +14,7 @@ class Legend < GraphElement
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def int_render(g)
|
17
|
+
return if not parent.get_global_option(:enable_legend,true)
|
17
18
|
|
18
19
|
w=g.newwin("legendpanel_mini")
|
19
20
|
g.setactivewindow(w)
|
@@ -24,11 +25,38 @@ class Legend < GraphElement
|
|
24
25
|
|
25
26
|
# toggle detail/mini legend
|
26
27
|
g.rectangle(@bounds.left-5,@bounds.top,5,5, {:href => "javascript:void(0);",
|
27
|
-
:onclick => "showDetailedLegend();"
|
28
|
-
|
28
|
+
:onclick => "showDetailedLegend();",
|
29
|
+
:fill => "white",
|
30
|
+
:stroke => "none",
|
31
|
+
:gerbiltooltip1 => "Click to show a detailed legend" })
|
32
|
+
|
33
|
+
g.textout(@bounds.left-7,@bounds.top+5,'+', {'font-size' => '9', :href => "javascript:void(0);",
|
34
|
+
:onclick => "showDetailedLegend();", :stroke => '#DDDDDD' ,
|
35
|
+
:gerbiltooltip1 => "Click to show a detailed legend" })
|
36
|
+
|
37
|
+
# shift
|
38
|
+
g.rectangle(@bounds.left-5,@bounds.top+15,5,5, {:href => "javascript:void(0);",
|
39
|
+
:onclick => "shiftLegend(#{-@bounds.left+50});",
|
40
|
+
:fill => "white",
|
41
|
+
:stroke => "none",
|
42
|
+
:gerbiltooltip1 => "Click to move legend to left side" })
|
43
|
+
g.textout(@bounds.left-7,@bounds.top+15,'<', {'font-size' => '9', :href => "javascript:void(0);",
|
44
|
+
:onclick => "shiftLegend(#{-@bounds.left+50});", :stroke => '#DDDDDD',
|
45
|
+
:gerbiltooltip1 => "Click to move legend to left side" })
|
46
|
+
|
47
|
+
|
48
|
+
# shift
|
49
|
+
g.rectangle(@bounds.left-5,@bounds.top+25,5,5, {:href => "javascript:void(0);",
|
50
|
+
:onclick => "shiftLegend(0);",
|
51
|
+
:fill => "white",
|
52
|
+
:stroke => "none" ,
|
53
|
+
:gerbiltooltip1 => "Click to move legend to right side"})
|
54
|
+
g.textout(@bounds.left-7,@bounds.top+25,'>', {'font-size' => '9', :href => "javascript:void(0);",
|
55
|
+
:onclick => "shiftLegend(0);", :stroke => '#DDDDDD',
|
56
|
+
:gerbiltooltip1 => "Click to move legend to right side"})
|
29
57
|
rbox = Rect.new
|
30
58
|
rbox.initfrom(@bounds)
|
31
|
-
rbox.left +=
|
59
|
+
rbox.left += 20
|
32
60
|
rbox.right = rbox.left + 10
|
33
61
|
rbox.top += 2
|
34
62
|
rbox.bottom = rbox.top+10
|
@@ -41,9 +69,18 @@ class Legend < GraphElement
|
|
41
69
|
opts.store(:gerbiltooltip1, mod.name)
|
42
70
|
opts.store(:gerbiltooltip2, "Latest Value = #{mod.latest_val}")
|
43
71
|
|
44
|
-
|
45
|
-
|
46
|
-
|
72
|
+
# Selector
|
73
|
+
ctlbox = Rect.new
|
74
|
+
ctlbox.initfrom(rbox)
|
75
|
+
ctlbox.offset_x(-16)
|
76
|
+
ctlbox.deflate(1)
|
77
|
+
g.circle_r(ctlbox, {:id => "lbx#{i}", :fill => 'gray', :stroke => 'none', :href => "javascript:void(0);", :onclick => "ToggleVisibility(#{i})"})
|
78
|
+
|
79
|
+
boxopts = {:id => "item#{i}"}
|
80
|
+
boxopts.store(:fill , color_for_id(i)) if i > 10
|
81
|
+
g.rectangle_r(rbox, boxopts )
|
82
|
+
g.textout(rbox.right+5, rbox.bottom-2, mod.name,opts)
|
83
|
+
|
47
84
|
rbox.top += 10 + 5
|
48
85
|
rbox.bottom = rbox.top+10
|
49
86
|
|
@@ -9,7 +9,23 @@ class MarkBand < GraphElement
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def int_render(g)
|
12
|
-
|
12
|
+
|
13
|
+
return unless parent.modelgroup.has_x_markers?
|
14
|
+
|
15
|
+
range_options_x = parent.get_global_option(:scaling_x,:auto)
|
16
|
+
range_options_y = parent.get_global_option(:scaling_y,:auto)
|
17
|
+
|
18
|
+
rx = parent.modelgroup.effective_range_x(range_options_x)
|
19
|
+
ry = parent.modelgroup.effective_range_y(range_options_y)
|
20
|
+
|
21
|
+
mx_arr = parent.modelgroup.x_markers
|
22
|
+
|
23
|
+
band_r=@bounds.clone
|
24
|
+
band_r.left = scale_x mx_arr[0],rx
|
25
|
+
band_r.right = scale_x mx_arr[1],rx
|
26
|
+
|
27
|
+
g.rectangle_r(band_r, {:class => 'x_mk'})
|
28
|
+
|
13
29
|
end
|
14
30
|
end
|
15
31
|
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module GerbilCharts::Surfaces
|
2
|
+
|
3
|
+
# Matrix Surface
|
4
|
+
# Conversation Chart is drawn
|
5
|
+
#
|
6
|
+
class MatrixSurface < Surface
|
7
|
+
|
8
|
+
def initialize(opts={})
|
9
|
+
super(opts)
|
10
|
+
end
|
11
|
+
|
12
|
+
def int_render(g)
|
13
|
+
# any filters ?
|
14
|
+
if parent.get_global_option(:filter,false)
|
15
|
+
g.curr_win.add_options({:filter => "url(##{parent.get_global_option(:filter,false)})" })
|
16
|
+
end
|
17
|
+
|
18
|
+
# An Outer Rectangle
|
19
|
+
g.rectangle(@bounds.left, @bounds.top, @bounds.right, @bounds.bottom)
|
20
|
+
|
21
|
+
# Adding horizontal and vertical lines to the rectangle
|
22
|
+
# Vertical Lines
|
23
|
+
vline_spacing = @bounds.width / (parent.modelgroup.zendlen+1)
|
24
|
+
vline_next = 0
|
25
|
+
for i in 0...(parent.modelgroup.zendlen)
|
26
|
+
g.line(vline_spacing+vline_next,@bounds.top,vline_spacing+vline_next,@bounds.bottom,:class => "gridlinev")
|
27
|
+
vline_next += vline_spacing
|
28
|
+
end
|
29
|
+
|
30
|
+
# Horizontal Lines
|
31
|
+
hline_spacing = (@bounds.height)/ (parent.modelgroup.aendlen+1)
|
32
|
+
hline_next = @bounds.top
|
33
|
+
for i in 0...(parent.modelgroup.aendlen)
|
34
|
+
g.line(@bounds.left,hline_spacing+hline_next,@bounds.right,hline_spacing+hline_next,:class => "gridlineh")
|
35
|
+
hline_next += hline_spacing
|
36
|
+
end
|
37
|
+
|
38
|
+
# X - Axis
|
39
|
+
aend = hline_spacing/2 + @bounds.top
|
40
|
+
parent.modelgroup.aenduniq.each_with_index do |item,i|
|
41
|
+
g.textout(@bounds.left,@bounds.left+(hline_spacing)+aend,item, {:class => "elementlabel"})
|
42
|
+
aend += hline_spacing
|
43
|
+
end
|
44
|
+
|
45
|
+
# Y - Axis
|
46
|
+
zend = vline_spacing/2
|
47
|
+
parent.modelgroup.zenduniq.each_with_index do |item,i|
|
48
|
+
xpos = @bounds.left+vline_spacing+zend
|
49
|
+
ypos = @bounds.left+(hline_spacing/2)+@bounds.top
|
50
|
+
opts = {:class => "elementlabel",
|
51
|
+
"text-anchor" => "middle",
|
52
|
+
:transform => "rotate(-45 #{xpos} #{ypos})"}
|
53
|
+
g.textout(@bounds.left+vline_spacing+zend,@bounds.left+(hline_spacing/2)+@bounds.top,item,opts)
|
54
|
+
zend += vline_spacing
|
55
|
+
end
|
56
|
+
|
57
|
+
# Drawing circle based on the values
|
58
|
+
cy_value = 0
|
59
|
+
parent.modelgroup.hash.each_pair do |key,value|
|
60
|
+
cx_value = 0
|
61
|
+
value.each_with_index do |item,i|
|
62
|
+
if item == 0
|
63
|
+
opts = {:id => "item20"}
|
64
|
+
else
|
65
|
+
opts = {:id => "item#{i}"}
|
66
|
+
end
|
67
|
+
# Adding Tool Tips
|
68
|
+
string ="(" + key + "," + parent.modelgroup.zenduniq[i] +")"
|
69
|
+
if parent.get_global_option(:auto_tooltips,false)
|
70
|
+
opts.merge!(:onmouseover => "OpacityDown(evt)", :onmouseout => "OpacityUp(evt)")
|
71
|
+
opts.store(:gerbiltooltip1, string)
|
72
|
+
opts.store(:gerbiltooltip2, "Val = #{item}")
|
73
|
+
end
|
74
|
+
# Calculating Radius of the circle based on values
|
75
|
+
radius = (((item.to_f)/parent.modelgroup.sort[0].to_f).to_f * (hline_spacing/2)).to_f
|
76
|
+
radius = radius * parent.modelgroup.multiply_factor
|
77
|
+
g.addshape(GerbilCharts::SVGDC::SVGCircle.new(@bounds.left+vline_spacing+(vline_spacing/2)+cx_value,
|
78
|
+
@bounds.left+hline_spacing+(hline_spacing/2)+cy_value+@bounds.top,
|
79
|
+
radius),
|
80
|
+
opts)
|
81
|
+
cx_value += vline_spacing
|
82
|
+
end
|
83
|
+
cy_value += hline_spacing
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -5,116 +5,129 @@ class PieSurface < Surface
|
|
5
5
|
|
6
6
|
MIN_INSIDE_LABEL_ANGLE = 8 # do not draw outer label for small angles
|
7
7
|
MIN_OUTSIDE_LABEL_ANGLE = 3 # do not draw inner percentages (pie slice) for small angles
|
8
|
-
|
9
|
-
OUTER_OFFSET_Y = 10
|
8
|
+
OUTER_OFFSET_X = 10 # x - distance offset from pie boundary
|
9
|
+
OUTER_OFFSET_Y = 10 # y - y offset from pie boundary
|
10
10
|
LEFT_LABEL_OFFSET = 30 # labels on the left of the PIE offset by this (90-270 deg)
|
11
|
+
PERCENT_OFFSET_X = -4 # pull slice label this much (tweak)
|
11
12
|
|
12
|
-
|
13
|
+
def initialize(opts={})
|
13
14
|
super(opts)
|
14
15
|
end
|
15
16
|
|
16
|
-
|
17
|
+
def int_render(g)
|
17
18
|
g.rectangle_r(@bounds, {:class => @class})
|
18
19
|
end
|
19
20
|
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
22
|
+
def int_render(g)
|
23
|
+
|
24
|
+
# bail out of empty models quickly
|
25
|
+
if parent.modelgroup.empty?
|
26
|
+
g.textout(@bounds.left + @bounds.width/2, @bounds.height/2,
|
27
|
+
parent.modelgroup.empty_caption,{"text-anchor" => "middle"})
|
28
|
+
return
|
29
|
+
end
|
30
|
+
|
31
|
+
# any filters ?
|
32
|
+
if parent.get_global_option(:filter,false)
|
33
|
+
g.curr_win.add_options({:filter => "url(##{parent.get_global_option(:filter,false)})" })
|
34
|
+
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
radius = @bounds.height * 0.35
|
42
|
-
radius_label = radius + 10
|
43
|
-
cx = @bounds.width/2
|
44
|
-
cy = @bounds.height/2
|
45
|
-
current_pos_x = cx + radius
|
46
|
-
current_pos_y = cy
|
47
|
-
|
48
|
-
# draw each slice
|
49
|
-
tot_angle =0
|
50
|
-
parent.modelgroup.each_model_with_index do | mod, i|
|
51
|
-
|
52
|
-
mod_angle = ((mod.latest_val*360).to_f)/(y_total.to_f)
|
53
|
-
half_angle = tot_angle + mod_angle/2
|
54
|
-
tot_angle = tot_angle + mod_angle
|
55
|
-
|
56
|
-
new_pos_x = cx + (Math.cos((Math::PI/180)*tot_angle)*radius)
|
57
|
-
new_pos_y = cy + (Math.sin((Math::PI/180)*tot_angle)*radius)
|
58
|
-
label_pos_x = cx + (Math.cos((Math::PI/180)*half_angle)*radius_label)
|
59
|
-
label_pos_y = cy + (Math.sin((Math::PI/180)*half_angle)*radius_label)
|
60
|
-
|
61
|
-
percent_mod = ((mod.latest_val*360).to_i/y_total.to_i)
|
62
|
-
percent = (percent_mod*100/360)
|
63
|
-
percent_pos_x = cx + ((Math.cos((Math::PI/180)*half_angle)*radius_label)*0.6)
|
64
|
-
percent_pos_y = cy + ((Math.sin((Math::PI/180)*half_angle)*radius_label)*0.6)
|
65
|
-
|
66
|
-
|
67
|
-
# if tooltips enabled, user can position on pie slice
|
68
|
-
opts = {:id => "item#{i}"}
|
69
|
-
if parent.get_global_option(:auto_tooltips,false)
|
70
|
-
opts.merge!(:onmouseover => "OpacityDown(evt)", :onmouseout => "OpacityUp(evt)")
|
71
|
-
opts.store(:gerbiltooltip1, mod.name)
|
72
|
-
opts.store(:gerbiltooltip2, "Val = #{mod.latest_formatted_val}")
|
73
|
-
end
|
74
|
-
opts.merge!(:href => mod.href) if mod.hasHref?
|
36
|
+
# compute totals
|
37
|
+
y_total = 0
|
38
|
+
parent.modelgroup.each_model_with_index do | mod, i|
|
39
|
+
y_total = y_total + mod.latest_val
|
40
|
+
end
|
75
41
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
42
|
+
radius = @bounds.height * 0.35
|
43
|
+
radius_label = radius + 10
|
44
|
+
cx = @bounds.width/2
|
45
|
+
cy = @bounds.height/2
|
46
|
+
current_pos_x = cx + radius
|
47
|
+
current_pos_y = cy
|
48
|
+
|
49
|
+
# draw each slice
|
50
|
+
tot_angle =0
|
51
|
+
last_label_pos_y=0
|
52
|
+
parent.modelgroup.each_model_with_index do | mod, i|
|
53
|
+
mod_angle = ((mod.latest_val*360).to_f)/(y_total.to_f)
|
54
|
+
half_angle = tot_angle + mod_angle/2
|
55
|
+
tot_angle = tot_angle + mod_angle
|
56
|
+
|
57
|
+
new_pos_x = cx + (Math.cos((Math::PI/180)*tot_angle)*radius)
|
58
|
+
new_pos_y = cy + (Math.sin((Math::PI/180)*tot_angle)*radius)
|
59
|
+
label_pos_x = cx + (Math.cos((Math::PI/180)*half_angle)*radius_label)
|
60
|
+
label_pos_y = cy + (Math.sin((Math::PI/180)*half_angle)*radius_label)
|
80
61
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
if(percent > MIN_INSIDE_LABEL_ANGLE)
|
107
|
-
if(tot_angle > 270)
|
108
|
-
g.textout(percent_pos_x,percent_pos_y+15, "#{percent}%", :class => 'elementvalue')
|
109
|
-
else
|
110
|
-
g.textout(percent_pos_x,percent_pos_y, "#{percent}%", :class => 'elementvalue')
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
current_pos_x = new_pos_x
|
115
|
-
current_pos_y = new_pos_y
|
62
|
+
percent_mod = ((mod.latest_val*360).to_i/y_total.to_i)
|
63
|
+
percent = (percent_mod*100/360)
|
64
|
+
percent_pos_x = cx + ((Math.cos((Math::PI/180)*half_angle)*radius_label)*0.6) + PERCENT_OFFSET_X
|
65
|
+
percent_pos_y = cy + ((Math.sin((Math::PI/180)*half_angle)*radius_label)*0.6)
|
66
|
+
|
67
|
+
|
68
|
+
# if tooltips enabled, user can position on pie slice
|
69
|
+
opts = {:id => "item#{i}"}
|
70
|
+
if parent.get_global_option(:auto_tooltips,false)
|
71
|
+
opts.merge!(:onmouseover => "OpacityDown(evt)", :onmouseout => "OpacityUp(evt)")
|
72
|
+
opts.store(:gerbiltooltip1, mod.name)
|
73
|
+
opts.store(:gerbiltooltip2, "#{mod.latest_formatted_val}")
|
74
|
+
end
|
75
|
+
opts.merge!(:href => mod.href) if mod.hasHref?
|
76
|
+
|
77
|
+
# draw a circle if there is only one data
|
78
|
+
if (mod_angle >= 360)
|
79
|
+
g.addshape(GerbilCharts::SVGDC::SVGCircle.new(cx,cy,radius),opts)
|
80
|
+
else
|
81
|
+
# draw the slice
|
82
|
+
g.addshape(GerbilCharts::SVGDC::SVGArc.new(cx,cy,radius,radius,
|
83
|
+
current_pos_x,current_pos_y,
|
84
|
+
new_pos_x,new_pos_y,mod_angle),
|
85
|
+
opts)
|
86
|
+
end
|
116
87
|
|
88
|
+
elementlabel_clsn = [ [30,'elementlabel_large'], [60, 'elementlabel_huge'] ].inject('elementlabel') do |mem,it|
|
89
|
+
percent > it[0] ? it[1]:mem
|
90
|
+
end
|
91
|
+
|
92
|
+
if(percent >= MIN_OUTSIDE_LABEL_ANGLE)
|
93
|
+
if(tot_angle > 270)
|
94
|
+
if(label_pos_x > cx ) && (label_pos_y > cy )
|
95
|
+
g.textout(label_pos_x,label_pos_y+OUTER_OFFSET_Y, "#{mod.name}", :class => elementlabel_clsn )
|
96
|
+
else
|
97
|
+
label_pos_y_off = (last_label_pos_y-label_pos_y) < 5 ? 5:0
|
98
|
+
g.textout( label_pos_x+OUTER_OFFSET_X, label_pos_y+OUTER_OFFSET_Y+label_pos_y_off, "#{mod.name}", :class => elementlabel_clsn )
|
99
|
+
end
|
100
|
+
elsif(label_pos_x > cx ) && (label_pos_y > cy)
|
101
|
+
g.textout( label_pos_x, label_pos_y+OUTER_OFFSET_Y, "#{mod.name}", :class => elementlabel_clsn )
|
102
|
+
else
|
103
|
+
g.textout( label_pos_x-LEFT_LABEL_OFFSET, label_pos_y+OUTER_OFFSET_Y, " #{mod.name}", :class => elementlabel_clsn )
|
104
|
+
end
|
117
105
|
end
|
106
|
+
|
107
|
+
|
108
|
+
if(percent > MIN_INSIDE_LABEL_ANGLE)
|
109
|
+
elementvalue_clsn = [ [30,'elementvalue_large'], [60, 'elementvalue_huge'] ].inject('elementvalue') do |mem,it|
|
110
|
+
percent > it[0] ? it[1]:mem
|
111
|
+
end
|
112
|
+
if (label_pos_y-percent_pos_y).abs < 10
|
113
|
+
percent_pos_y -= 10 if percent_pos_y < label_pos_y
|
114
|
+
percent_pos_y += 10 if percent_pos_y > label_pos_y
|
115
|
+
end
|
116
|
+
if(tot_angle > 270)
|
117
|
+
g.textout(percent_pos_x,percent_pos_y+5, "#{percent}%", :class => elementvalue_clsn)
|
118
|
+
g.textout(percent_pos_x,percent_pos_y+15, "#{mod.latest_formatted_val}", :class => 'elementlabel' )
|
119
|
+
else
|
120
|
+
g.textout(percent_pos_x,percent_pos_y, "#{percent}%", :class => elementvalue_clsn)
|
121
|
+
g.textout(percent_pos_x,percent_pos_y+10, "#{mod.latest_formatted_val}", :class => 'elementlabel' )
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
current_pos_x = new_pos_x
|
126
|
+
current_pos_y = new_pos_y
|
127
|
+
|
128
|
+
last_label_pos_y = label_pos_y
|
118
129
|
end
|
130
|
+
end
|
131
|
+
|
119
132
|
end
|
120
133
|
end
|