schapht-gruff 0.3.5
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 +111 -0
- data/MIT-LICENSE +21 -0
- data/Manifest.txt +79 -0
- data/README.txt +40 -0
- data/Rakefile +55 -0
- data/assets/bubble.png +0 -0
- data/assets/city_scene/background/0000.png +0 -0
- data/assets/city_scene/background/0600.png +0 -0
- data/assets/city_scene/background/2000.png +0 -0
- data/assets/city_scene/clouds/cloudy.png +0 -0
- data/assets/city_scene/clouds/partly_cloudy.png +0 -0
- data/assets/city_scene/clouds/stormy.png +0 -0
- data/assets/city_scene/grass/default.png +0 -0
- data/assets/city_scene/haze/true.png +0 -0
- data/assets/city_scene/number_sample/1.png +0 -0
- data/assets/city_scene/number_sample/2.png +0 -0
- data/assets/city_scene/number_sample/default.png +0 -0
- data/assets/city_scene/sky/0000.png +0 -0
- data/assets/city_scene/sky/0200.png +0 -0
- data/assets/city_scene/sky/0400.png +0 -0
- data/assets/city_scene/sky/0600.png +0 -0
- data/assets/city_scene/sky/0800.png +0 -0
- data/assets/city_scene/sky/1000.png +0 -0
- data/assets/city_scene/sky/1200.png +0 -0
- data/assets/city_scene/sky/1400.png +0 -0
- data/assets/city_scene/sky/1500.png +0 -0
- data/assets/city_scene/sky/1700.png +0 -0
- data/assets/city_scene/sky/2000.png +0 -0
- data/assets/pc306715.jpg +0 -0
- data/assets/plastik/blue.png +0 -0
- data/assets/plastik/green.png +0 -0
- data/assets/plastik/red.png +0 -0
- data/init.rb +2 -0
- data/lib/gruff.rb +27 -0
- data/lib/gruff/accumulator_bar.rb +27 -0
- data/lib/gruff/area.rb +58 -0
- data/lib/gruff/bar.rb +84 -0
- data/lib/gruff/bar_conversion.rb +46 -0
- data/lib/gruff/base.rb +1112 -0
- data/lib/gruff/bullet.rb +109 -0
- data/lib/gruff/deprecated.rb +39 -0
- data/lib/gruff/line.rb +105 -0
- data/lib/gruff/mini/bar.rb +32 -0
- data/lib/gruff/mini/legend.rb +77 -0
- data/lib/gruff/mini/pie.rb +36 -0
- data/lib/gruff/mini/side_bar.rb +35 -0
- data/lib/gruff/net.rb +142 -0
- data/lib/gruff/photo_bar.rb +100 -0
- data/lib/gruff/pie.rb +124 -0
- data/lib/gruff/scene.rb +209 -0
- data/lib/gruff/side_bar.rb +115 -0
- data/lib/gruff/side_stacked_bar.rb +74 -0
- data/lib/gruff/spider.rb +130 -0
- data/lib/gruff/stacked_area.rb +67 -0
- data/lib/gruff/stacked_bar.rb +54 -0
- data/lib/gruff/stacked_mixin.rb +23 -0
- data/rails_generators/gruff/gruff_generator.rb +63 -0
- data/rails_generators/gruff/templates/controller.rb +32 -0
- data/rails_generators/gruff/templates/functional_test.rb +24 -0
- data/test/gruff_test_case.rb +123 -0
- data/test/test_accumulator_bar.rb +50 -0
- data/test/test_area.rb +134 -0
- data/test/test_bar.rb +283 -0
- data/test/test_base.rb +8 -0
- data/test/test_bullet.rb +26 -0
- data/test/test_legend.rb +68 -0
- data/test/test_line.rb +513 -0
- data/test/test_mini_bar.rb +32 -0
- data/test/test_mini_pie.rb +20 -0
- data/test/test_mini_side_bar.rb +37 -0
- data/test/test_net.rb +230 -0
- data/test/test_photo.rb +41 -0
- data/test/test_pie.rb +154 -0
- data/test/test_scene.rb +100 -0
- data/test/test_side_bar.rb +12 -0
- data/test/test_sidestacked_bar.rb +89 -0
- data/test/test_spider.rb +216 -0
- data/test/test_stacked_area.rb +52 -0
- data/test/test_stacked_bar.rb +52 -0
- metadata +160 -0
data/lib/gruff/bullet.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
class Gruff::Bullet < Gruff::Base
|
4
|
+
|
5
|
+
def initialize(target_width="400x40")
|
6
|
+
if not Numeric === target_width
|
7
|
+
geometric_width, geometric_height = target_width.split('x')
|
8
|
+
@columns = geometric_width.to_f
|
9
|
+
@rows = geometric_height.to_f
|
10
|
+
else
|
11
|
+
@columns = target_width.to_f
|
12
|
+
@rows = target_width.to_f / 5.0
|
13
|
+
end
|
14
|
+
|
15
|
+
initialize_ivars
|
16
|
+
|
17
|
+
reset_themes
|
18
|
+
theme_greyscale
|
19
|
+
@title_font_size = 20
|
20
|
+
end
|
21
|
+
|
22
|
+
def data(value, maximum_value, options={})
|
23
|
+
@value = value.to_f
|
24
|
+
@maximum_value = maximum_value.to_f
|
25
|
+
@options = options
|
26
|
+
@options.map { |k, v| @options[k] = v.to_f if v === Numeric }
|
27
|
+
end
|
28
|
+
|
29
|
+
# def setup_drawing
|
30
|
+
# # Maybe should be done in one of the following functions for more granularity.
|
31
|
+
# unless @has_data
|
32
|
+
# draw_no_data()
|
33
|
+
# return
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# normalize()
|
37
|
+
# setup_graph_measurements()
|
38
|
+
# sort_norm_data() if @sort # Sort norm_data with avg largest values set first (for display)
|
39
|
+
#
|
40
|
+
# draw_legend()
|
41
|
+
# draw_line_markers()
|
42
|
+
# draw_axis_labels()
|
43
|
+
# draw_title
|
44
|
+
# end
|
45
|
+
|
46
|
+
def draw
|
47
|
+
# TODO Left label
|
48
|
+
# TODO Bottom labels and markers
|
49
|
+
# @graph_bottom
|
50
|
+
# Calculations are off 800x???
|
51
|
+
|
52
|
+
@colors.reverse!
|
53
|
+
|
54
|
+
draw_title
|
55
|
+
|
56
|
+
@margin = 30.0
|
57
|
+
@thickness = @raw_rows / 6.0
|
58
|
+
@right_margin = @margin
|
59
|
+
@graph_left = @title_width * 1.3 rescue @margin # HACK Need to calculate real width
|
60
|
+
@graph_width = @raw_columns - @graph_left - @right_margin
|
61
|
+
@graph_height = @thickness * 3.0
|
62
|
+
|
63
|
+
# Background
|
64
|
+
@d = @d.fill @colors[0]
|
65
|
+
@d = @d.rectangle(@graph_left, 0, @graph_left + @graph_width, @graph_height)
|
66
|
+
|
67
|
+
[:high, :low].each_with_index do |indicator, index|
|
68
|
+
next unless @options.has_key?(indicator)
|
69
|
+
@d = @d.fill @colors[index + 1]
|
70
|
+
indicator_width_x = @graph_left + @graph_width * (@options[indicator] / @maximum_value)
|
71
|
+
@d = @d.rectangle(@graph_left, 0, indicator_width_x, @graph_height)
|
72
|
+
end
|
73
|
+
|
74
|
+
if @options.has_key?(:target)
|
75
|
+
@d = @d.fill @font_color
|
76
|
+
target_x = @graph_left + @graph_width * (@options[:target] / @maximum_value)
|
77
|
+
half_thickness = @thickness / 2.0
|
78
|
+
@d = @d.rectangle(target_x, half_thickness, target_x + half_thickness, @thickness * 2 + half_thickness)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Value
|
82
|
+
@d = @d.fill @font_color
|
83
|
+
@d = @d.rectangle(@graph_left, @thickness, @graph_left + @graph_width * (@value / @maximum_value), @thickness * 2)
|
84
|
+
|
85
|
+
@d.draw(@base_image)
|
86
|
+
end
|
87
|
+
|
88
|
+
def draw_title
|
89
|
+
return unless @title
|
90
|
+
|
91
|
+
@font_height = calculate_caps_height(scale_fontsize(@title_font_size))
|
92
|
+
@title_width = calculate_width(@title_font_size, @title)
|
93
|
+
|
94
|
+
@d.fill = @font_color
|
95
|
+
@d.font = @font if @font
|
96
|
+
@d.stroke('transparent')
|
97
|
+
@d.font_weight = NormalWeight
|
98
|
+
@d.pointsize = scale_fontsize(@title_font_size)
|
99
|
+
@d.gravity = NorthWestGravity
|
100
|
+
@d = @d.annotate_scaled(*[
|
101
|
+
@base_image,
|
102
|
+
1.0, 1.0,
|
103
|
+
@font_height/2, @font_height/2,
|
104
|
+
@title,
|
105
|
+
@scale
|
106
|
+
])
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
##
|
3
|
+
# A mixin for methods that need to be deleted or have been
|
4
|
+
# replaced by cleaner code.
|
5
|
+
|
6
|
+
module Gruff
|
7
|
+
module Deprecated
|
8
|
+
|
9
|
+
def scale_measurements
|
10
|
+
setup_graph_measurements
|
11
|
+
end
|
12
|
+
|
13
|
+
def total_height
|
14
|
+
@rows + 10
|
15
|
+
end
|
16
|
+
|
17
|
+
def graph_top
|
18
|
+
@graph_top * @scale
|
19
|
+
end
|
20
|
+
|
21
|
+
def graph_height
|
22
|
+
@graph_height * @scale
|
23
|
+
end
|
24
|
+
|
25
|
+
def graph_left
|
26
|
+
@graph_left * @scale
|
27
|
+
end
|
28
|
+
|
29
|
+
def graph_width
|
30
|
+
@graph_width * @scale
|
31
|
+
end
|
32
|
+
|
33
|
+
# TODO Should be calculate_graph_height
|
34
|
+
# def setup_graph_height
|
35
|
+
# @graph_height = @graph_bottom - @graph_top
|
36
|
+
# end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/lib/gruff/line.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
|
2
|
+
require File.dirname(__FILE__) + '/base'
|
3
|
+
|
4
|
+
##
|
5
|
+
# Here's how to make a Line graph:
|
6
|
+
#
|
7
|
+
# g = Gruff::Line.new
|
8
|
+
# g.title = "A Line Graph"
|
9
|
+
# g.data 'Fries', [20, 23, 19, 8]
|
10
|
+
# g.data 'Hamburgers', [50, 19, 99, 29]
|
11
|
+
# g.write("test/output/line.png")
|
12
|
+
#
|
13
|
+
# There are also other options described below, such as #baseline_value, #baseline_color, #hide_dots, and #hide_lines.
|
14
|
+
|
15
|
+
class Gruff::Line < Gruff::Base
|
16
|
+
|
17
|
+
# Draw a dashed line at the given value
|
18
|
+
attr_accessor :baseline_value
|
19
|
+
|
20
|
+
# Color of the baseline
|
21
|
+
attr_accessor :baseline_color
|
22
|
+
|
23
|
+
# Hide parts of the graph to fit more datapoints, or for a different appearance.
|
24
|
+
attr_accessor :hide_dots, :hide_lines
|
25
|
+
|
26
|
+
# Call with target pixel width of graph (800, 400, 300), and/or 'false' to omit lines (points only).
|
27
|
+
#
|
28
|
+
# g = Gruff::Line.new(400) # 400px wide with lines
|
29
|
+
#
|
30
|
+
# g = Gruff::Line.new(400, false) # 400px wide, no lines (for backwards compatibility)
|
31
|
+
#
|
32
|
+
# g = Gruff::Line.new(false) # Defaults to 800px wide, no lines (for backwards compatibility)
|
33
|
+
#
|
34
|
+
# The preferred way is to call hide_dots or hide_lines instead.
|
35
|
+
def initialize(*args)
|
36
|
+
raise ArgumentError, "Wrong number of arguments" if args.length > 2
|
37
|
+
if args.empty? or ((not Numeric === args.first) && (not String === args.first)) then
|
38
|
+
super()
|
39
|
+
else
|
40
|
+
super args.shift
|
41
|
+
end
|
42
|
+
|
43
|
+
@hide_dots = @hide_lines = false
|
44
|
+
@baseline_color = 'red'
|
45
|
+
@baseline_value = nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def draw
|
49
|
+
super
|
50
|
+
|
51
|
+
return unless @has_data
|
52
|
+
|
53
|
+
# Check to see if more than one datapoint was given. NaN can result otherwise.
|
54
|
+
@x_increment = (@column_count > 1) ? (@graph_width / (@column_count - 1).to_f) : @graph_width
|
55
|
+
|
56
|
+
if (defined?(@norm_baseline)) then
|
57
|
+
level = @graph_top + (@graph_height - @norm_baseline * @graph_height)
|
58
|
+
@d = @d.push
|
59
|
+
@d.stroke_color @baseline_color
|
60
|
+
@d.fill_opacity 0.0
|
61
|
+
@d.stroke_dasharray(10, 20)
|
62
|
+
@d.stroke_width 5
|
63
|
+
@d.line(@graph_left, level, @graph_left + @graph_width, level)
|
64
|
+
@d = @d.pop
|
65
|
+
end
|
66
|
+
|
67
|
+
@norm_data.each do |data_row|
|
68
|
+
prev_x = prev_y = nil
|
69
|
+
|
70
|
+
data_row[DATA_VALUES_INDEX].each_with_index do |data_point, index|
|
71
|
+
new_x = @graph_left + (@x_increment * index)
|
72
|
+
next if data_point.nil?
|
73
|
+
|
74
|
+
draw_label(new_x, index)
|
75
|
+
|
76
|
+
new_y = @graph_top + (@graph_height - data_point * @graph_height)
|
77
|
+
|
78
|
+
# Reset each time to avoid thin-line errors
|
79
|
+
@d = @d.stroke data_row[DATA_COLOR_INDEX]
|
80
|
+
@d = @d.fill data_row[DATA_COLOR_INDEX]
|
81
|
+
@d = @d.stroke_opacity 1.0
|
82
|
+
@d = @d.stroke_width clip_value_if_greater_than(@columns / (@norm_data.first[DATA_VALUES_INDEX].size * 4), 5.0)
|
83
|
+
|
84
|
+
if !@hide_lines and !prev_x.nil? and !prev_y.nil? then
|
85
|
+
@d = @d.line(prev_x, prev_y, new_x, new_y)
|
86
|
+
end
|
87
|
+
circle_radius = clip_value_if_greater_than(@columns / (@norm_data.first[DATA_VALUES_INDEX].size * 2.5), 5.0)
|
88
|
+
@d = @d.circle(new_x, new_y, new_x - circle_radius, new_y) unless @hide_dots
|
89
|
+
|
90
|
+
prev_x = new_x
|
91
|
+
prev_y = new_y
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
@d.draw(@base_image)
|
97
|
+
end
|
98
|
+
|
99
|
+
def normalize
|
100
|
+
@maximum_value = [@maximum_value.to_f, @baseline_value.to_f].max
|
101
|
+
super
|
102
|
+
@norm_baseline = (@baseline_value.to_f / @maximum_value.to_f) if @baseline_value
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
##
|
2
|
+
#
|
3
|
+
# Makes a small bar graph suitable for display at 200px or even smaller.
|
4
|
+
#
|
5
|
+
module Gruff
|
6
|
+
module Mini
|
7
|
+
|
8
|
+
class Bar < Gruff::Bar
|
9
|
+
|
10
|
+
include Gruff::Mini::Legend
|
11
|
+
|
12
|
+
def draw
|
13
|
+
@hide_legend = true
|
14
|
+
@hide_title = true
|
15
|
+
@hide_line_numbers = true
|
16
|
+
|
17
|
+
@marker_font_size = 50.0
|
18
|
+
@minimum_value = 0.0
|
19
|
+
@legend_font_size = 60.0
|
20
|
+
|
21
|
+
expand_canvas_for_vertical_legend
|
22
|
+
|
23
|
+
super
|
24
|
+
|
25
|
+
draw_vertical_legend
|
26
|
+
@d.draw(@base_image)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Gruff
|
2
|
+
module Mini
|
3
|
+
module Legend
|
4
|
+
|
5
|
+
##
|
6
|
+
# The canvas needs to be bigger so we can put the legend beneath it.
|
7
|
+
|
8
|
+
def expand_canvas_for_vertical_legend
|
9
|
+
@original_rows = @raw_rows
|
10
|
+
@rows += @data.length * calculate_caps_height(scale_fontsize(@legend_font_size)) * 1.7
|
11
|
+
render_background
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Draw the legend beneath the existing graph.
|
16
|
+
|
17
|
+
def draw_vertical_legend
|
18
|
+
|
19
|
+
@legend_labels = @data.collect {|item| item[Gruff::Base::DATA_LABEL_INDEX] }
|
20
|
+
|
21
|
+
legend_square_width = 40.0 # small square with color of this item
|
22
|
+
legend_square_margin = 10.0
|
23
|
+
@legend_left_margin = 100.0
|
24
|
+
legend_top_margin = 40.0
|
25
|
+
|
26
|
+
# May fix legend drawing problem at small sizes
|
27
|
+
@d.font = @font if @font
|
28
|
+
@d.pointsize = @legend_font_size
|
29
|
+
|
30
|
+
current_x_offset = @legend_left_margin
|
31
|
+
current_y_offset = @original_rows + legend_top_margin
|
32
|
+
|
33
|
+
debug { @d.line 0.0, current_y_offset, @raw_columns, current_y_offset }
|
34
|
+
|
35
|
+
@legend_labels.each_with_index do |legend_label, index|
|
36
|
+
|
37
|
+
# Draw label
|
38
|
+
@d.fill = @font_color
|
39
|
+
@d.font = @font if @font
|
40
|
+
@d.pointsize = scale_fontsize(@legend_font_size)
|
41
|
+
@d.stroke = 'transparent'
|
42
|
+
@d.font_weight = Magick::NormalWeight
|
43
|
+
@d.gravity = Magick::WestGravity
|
44
|
+
@d = @d.annotate_scaled( @base_image,
|
45
|
+
@raw_columns, 1.0,
|
46
|
+
current_x_offset + (legend_square_width * 1.7), current_y_offset,
|
47
|
+
truncate_legend_label(legend_label), @scale)
|
48
|
+
|
49
|
+
# Now draw box with color of this dataset
|
50
|
+
@d = @d.stroke 'transparent'
|
51
|
+
@d = @d.fill @data[index][Gruff::Base::DATA_COLOR_INDEX]
|
52
|
+
@d = @d.rectangle(current_x_offset,
|
53
|
+
current_y_offset - legend_square_width / 2.0,
|
54
|
+
current_x_offset + legend_square_width,
|
55
|
+
current_y_offset + legend_square_width / 2.0)
|
56
|
+
|
57
|
+
current_y_offset += calculate_caps_height(@legend_font_size) * 1.7
|
58
|
+
end
|
59
|
+
@color_index = 0
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Shorten long labels so they will fit on the canvas.
|
64
|
+
#
|
65
|
+
# Department of Hu...
|
66
|
+
|
67
|
+
def truncate_legend_label(label)
|
68
|
+
truncated_label = label.to_s
|
69
|
+
while calculate_width(scale_fontsize(@legend_font_size), truncated_label) > (@columns - @legend_left_margin - @right_margin) && (truncated_label.length > 1)
|
70
|
+
truncated_label = truncated_label[0..truncated_label.length-2]
|
71
|
+
end
|
72
|
+
truncated_label + (truncated_label.length < label.to_s.length ? "…" : '')
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
##
|
2
|
+
#
|
3
|
+
# Makes a small pie graph suitable for display at 200px or even smaller.
|
4
|
+
#
|
5
|
+
module Gruff
|
6
|
+
module Mini
|
7
|
+
|
8
|
+
class Pie < Gruff::Pie
|
9
|
+
|
10
|
+
include Gruff::Mini::Legend
|
11
|
+
|
12
|
+
def initialize_ivars
|
13
|
+
super
|
14
|
+
|
15
|
+
@hide_legend = true
|
16
|
+
@hide_title = true
|
17
|
+
@hide_line_numbers = true
|
18
|
+
|
19
|
+
@marker_font_size = 60.0
|
20
|
+
@legend_font_size = 60.0
|
21
|
+
end
|
22
|
+
|
23
|
+
def draw
|
24
|
+
expand_canvas_for_vertical_legend
|
25
|
+
|
26
|
+
super
|
27
|
+
|
28
|
+
draw_vertical_legend
|
29
|
+
|
30
|
+
@d.draw(@base_image)
|
31
|
+
end # def draw
|
32
|
+
|
33
|
+
end # class Pie
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
##
|
2
|
+
#
|
3
|
+
# Makes a small pie graph suitable for display at 200px or even smaller.
|
4
|
+
#
|
5
|
+
module Gruff
|
6
|
+
module Mini
|
7
|
+
|
8
|
+
class SideBar < Gruff::SideBar
|
9
|
+
|
10
|
+
include Gruff::Mini::Legend
|
11
|
+
|
12
|
+
def initialize_ivars
|
13
|
+
super
|
14
|
+
@hide_legend = true
|
15
|
+
@hide_title = true
|
16
|
+
@hide_line_numbers = true
|
17
|
+
|
18
|
+
@marker_font_size = 50.0
|
19
|
+
@legend_font_size = 50.0
|
20
|
+
end
|
21
|
+
|
22
|
+
def draw
|
23
|
+
expand_canvas_for_vertical_legend
|
24
|
+
|
25
|
+
super
|
26
|
+
|
27
|
+
draw_vertical_legend
|
28
|
+
|
29
|
+
@d.draw(@base_image)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
data/lib/gruff/net.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
|
2
|
+
require File.dirname(__FILE__) + '/base'
|
3
|
+
|
4
|
+
# Experimental!!! See also the Spider graph.
|
5
|
+
class Gruff::Net < Gruff::Base
|
6
|
+
|
7
|
+
# Hide parts of the graph to fit more datapoints, or for a different appearance.
|
8
|
+
attr_accessor :hide_dots
|
9
|
+
|
10
|
+
def initialize(*args)
|
11
|
+
super
|
12
|
+
|
13
|
+
@hide_dots = false
|
14
|
+
end
|
15
|
+
|
16
|
+
def draw
|
17
|
+
|
18
|
+
super
|
19
|
+
|
20
|
+
return unless @has_data
|
21
|
+
|
22
|
+
@radius = @graph_height / 2.0
|
23
|
+
@center_x = @graph_left + (@graph_width / 2.0)
|
24
|
+
@center_y = @graph_top + (@graph_height / 2.0) - 10 # Move graph up a bit
|
25
|
+
|
26
|
+
@x_increment = @graph_width / (@column_count - 1).to_f
|
27
|
+
circle_radius = clip_value_if_greater_than(@columns / (@norm_data.first[DATA_VALUES_INDEX].size * 2.5), 5.0)
|
28
|
+
|
29
|
+
@d = @d.stroke_opacity 1.0
|
30
|
+
@d = @d.stroke_width clip_value_if_greater_than(@columns / (@norm_data.first[DATA_VALUES_INDEX].size * 4), 5.0)
|
31
|
+
|
32
|
+
if (defined?(@norm_baseline)) then
|
33
|
+
level = @graph_top + (@graph_height - @norm_baseline * @graph_height)
|
34
|
+
@d = @d.push
|
35
|
+
@d.stroke_color @baseline_color
|
36
|
+
@d.fill_opacity 0.0
|
37
|
+
@d.stroke_dasharray(10, 20)
|
38
|
+
@d.stroke_width 5
|
39
|
+
@d.line(@graph_left, level, @graph_left + @graph_width, level)
|
40
|
+
@d = @d.pop
|
41
|
+
end
|
42
|
+
|
43
|
+
@norm_data.each do |data_row|
|
44
|
+
prev_x = prev_y = nil
|
45
|
+
@d = @d.stroke data_row[DATA_COLOR_INDEX]
|
46
|
+
@d = @d.fill data_row[DATA_COLOR_INDEX]
|
47
|
+
|
48
|
+
data_row[DATA_VALUES_INDEX].each_with_index do |data_point, index|
|
49
|
+
next if data_point.nil?
|
50
|
+
|
51
|
+
rad_pos = index * Math::PI * 2 / @column_count
|
52
|
+
point_distance = data_point * @radius
|
53
|
+
start_x = @center_x + Math::sin(rad_pos) * point_distance
|
54
|
+
start_y = @center_y - Math::cos(rad_pos) * point_distance
|
55
|
+
|
56
|
+
next_index = index + 1 < data_row[DATA_VALUES_INDEX].length ? index + 1 : 0
|
57
|
+
|
58
|
+
next_rad_pos = next_index * Math::PI * 2 / @column_count
|
59
|
+
next_point_distance = data_row[DATA_VALUES_INDEX][next_index] * @radius
|
60
|
+
end_x = @center_x + Math::sin(next_rad_pos) * next_point_distance
|
61
|
+
end_y = @center_y - Math::cos(next_rad_pos) * next_point_distance
|
62
|
+
|
63
|
+
@d = @d.line(start_x, start_y, end_x, end_y)
|
64
|
+
|
65
|
+
@d = @d.circle(start_x, start_y, start_x - circle_radius, start_y) unless @hide_dots
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
@d.draw(@base_image)
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
# the lines connecting in the center, with the first line vertical
|
75
|
+
def draw_line_markers
|
76
|
+
return if @hide_line_markers
|
77
|
+
|
78
|
+
|
79
|
+
# have to do this here (AGAIN)... see draw() in this class
|
80
|
+
# because this funtion is called before the @radius, @center_x and @center_y are set
|
81
|
+
@radius = @graph_height / 2.0
|
82
|
+
@center_x = @graph_left + (@graph_width / 2.0)
|
83
|
+
@center_y = @graph_top + (@graph_height / 2.0) - 10 # Move graph up a bit
|
84
|
+
|
85
|
+
|
86
|
+
# Draw horizontal line markers and annotate with numbers
|
87
|
+
@d = @d.stroke(@marker_color)
|
88
|
+
@d = @d.stroke_width 1
|
89
|
+
|
90
|
+
|
91
|
+
(0..@column_count-1).each do |index|
|
92
|
+
rad_pos = index * Math::PI * 2 / @column_count
|
93
|
+
|
94
|
+
@d = @d.line(@center_x, @center_y, @center_x + Math::sin(rad_pos) * @radius, @center_y - Math::cos(rad_pos) * @radius)
|
95
|
+
|
96
|
+
|
97
|
+
marker_label = labels[index] ? labels[index].to_s : '000'
|
98
|
+
|
99
|
+
draw_label(@center_x, @center_y, rad_pos * 360 / (2 * Math::PI), @radius, marker_label)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def draw_label(center_x, center_y, angle, radius, amount)
|
106
|
+
r_offset = 1.1
|
107
|
+
x_offset = center_x # + 15 # The label points need to be tweaked slightly
|
108
|
+
y_offset = center_y # + 0 # This one doesn't though
|
109
|
+
x = x_offset + (radius * r_offset * Math.sin(angle.deg2rad))
|
110
|
+
y = y_offset - (radius * r_offset * Math.cos(angle.deg2rad))
|
111
|
+
|
112
|
+
# Draw label
|
113
|
+
@d.fill = @marker_color
|
114
|
+
@d.font = @font if @font
|
115
|
+
@d.pointsize = scale_fontsize(20)
|
116
|
+
@d.stroke = 'transparent'
|
117
|
+
@d.font_weight = BoldWeight
|
118
|
+
s = angle.deg2rad / (2*Math::PI)
|
119
|
+
@d.gravity = SouthGravity if s >= 0.96 or s < 0.04
|
120
|
+
@d.gravity = SouthWestGravity if s >= 0.04 or s < 0.21
|
121
|
+
@d.gravity = WestGravity if s >= 0.21 or s < 0.29
|
122
|
+
@d.gravity = NorthWestGravity if s >= 0.29 or s < 0.46
|
123
|
+
@d.gravity = NorthGravity if s >= 0.46 or s < 0.54
|
124
|
+
@d.gravity = NorthEastGravity if s >= 0.54 or s < 0.71
|
125
|
+
@d.gravity = EastGravity if s >= 0.71 or s < 0.79
|
126
|
+
@d.gravity = SouthEastGravity if s >= 0.79 or s < 0.96
|
127
|
+
# @d.gravity = NorthGravity
|
128
|
+
@d.annotate_scaled(@base_image, 0, 0, x, y, amount, @scale)
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
# # This method is already in Float
|
134
|
+
# class Float
|
135
|
+
# # Used for degree => radian conversions
|
136
|
+
# def deg2rad
|
137
|
+
# self * (Math::PI/180.0)
|
138
|
+
# end
|
139
|
+
# end
|
140
|
+
|
141
|
+
|
142
|
+
|