gerbilcharts 0.2.13 → 0.5.9
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/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
|