gruffy 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ ##
2
+ #
3
+ # Makes a small bar graph suitable for display at 200px or even smaller.
4
+ #
5
+ module Gruffy
6
+ module Mini
7
+
8
+ class Bar < Gruffy::Bar
9
+
10
+ include Gruffy::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 = 50.0
20
+ @minimum_value = 0.0
21
+ @maximum_value = 0.0
22
+ @legend_font_size = 60.0
23
+ end
24
+
25
+ def draw
26
+ expand_canvas_for_vertical_legend
27
+
28
+ super
29
+
30
+ draw_vertical_legend
31
+ @d.draw(@base_image)
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,114 @@
1
+ module Gruffy
2
+ module Mini
3
+ module Legend
4
+
5
+ attr_accessor :hide_mini_legend, :legend_position
6
+
7
+ def initialize(*)
8
+ @hide_mini_legend = false
9
+ @legend_position = nil
10
+ super
11
+ end
12
+
13
+ ##
14
+ # The canvas needs to be bigger so we can put the legend beneath it.
15
+
16
+ def expand_canvas_for_vertical_legend
17
+ return if @hide_mini_legend
18
+
19
+ @legend_labels = @data.collect {|item| item[Gruffy::Base::DATA_LABEL_INDEX] }
20
+
21
+ legend_height = scale_fontsize(
22
+ @data.length * calculate_line_height +
23
+ @top_margin + @bottom_margin)
24
+
25
+ @original_rows = @raw_rows
26
+ @original_columns = @raw_columns
27
+
28
+ case @legend_position
29
+ when :right then
30
+ @rows = [@rows, legend_height].max
31
+ @columns += calculate_legend_width + @left_margin
32
+ else
33
+ @rows += @data.length * calculate_caps_height(scale_fontsize(@legend_font_size)) * 1.7
34
+ end
35
+ render_background
36
+ end
37
+
38
+ def calculate_line_height
39
+ calculate_caps_height(@legend_font_size) * 1.7
40
+ end
41
+
42
+ def calculate_legend_width
43
+ width = @legend_labels.map { |label| calculate_width(@legend_font_size, label) }.max
44
+ scale_fontsize(width + 40*1.7)
45
+ end
46
+
47
+ ##
48
+ # Draw the legend beneath the existing graph.
49
+
50
+ def draw_vertical_legend
51
+ return if @hide_mini_legend
52
+
53
+ legend_square_width = 40.0 # small square with color of this item
54
+ @legend_left_margin = 100.0
55
+ legend_top_margin = 40.0
56
+
57
+ # May fix legend drawing problem at small sizes
58
+ @d.font = @font if @font
59
+ @d.pointsize = @legend_font_size
60
+
61
+ case @legend_position
62
+ when :right then
63
+ current_x_offset = @original_columns + @left_margin
64
+ current_y_offset = @top_margin + legend_top_margin
65
+ else
66
+ current_x_offset = @legend_left_margin
67
+ current_y_offset = @original_rows + legend_top_margin
68
+ end
69
+
70
+ debug { @d.line 0.0, current_y_offset, @raw_columns, current_y_offset }
71
+
72
+ @legend_labels.each_with_index do |legend_label, index|
73
+
74
+ # Draw label
75
+ @d.fill = @font_color
76
+ @d.font = @font if @font
77
+ @d.pointsize = scale_fontsize(@legend_font_size)
78
+ @d.stroke = 'transparent'
79
+ @d.font_weight = Magick::NormalWeight
80
+ @d.gravity = Magick::WestGravity
81
+ @d = @d.annotate_scaled( @base_image,
82
+ @raw_columns, 1.0,
83
+ current_x_offset + (legend_square_width * 1.7), current_y_offset,
84
+ truncate_legend_label(legend_label), @scale)
85
+
86
+ # Now draw box with color of this dataset
87
+ @d = @d.stroke 'transparent'
88
+ @d = @d.fill @data[index][Gruffy::Base::DATA_COLOR_INDEX]
89
+ @d = @d.rectangle(current_x_offset,
90
+ current_y_offset - legend_square_width / 2.0,
91
+ current_x_offset + legend_square_width,
92
+ current_y_offset + legend_square_width / 2.0)
93
+
94
+ current_y_offset += calculate_line_height
95
+ end
96
+ @color_index = 0
97
+ end
98
+
99
+ ##
100
+ # Shorten long labels so they will fit on the canvas.
101
+ #
102
+ # Department of Hu...
103
+
104
+ def truncate_legend_label(label)
105
+ truncated_label = label.to_s
106
+ while calculate_width(scale_fontsize(@legend_font_size), truncated_label) > (@columns - @legend_left_margin - @right_margin) && (truncated_label.length > 1)
107
+ truncated_label = truncated_label[0..truncated_label.length-2]
108
+ end
109
+ truncated_label + (truncated_label.length < label.to_s.length ? "..." : '')
110
+ end
111
+
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,36 @@
1
+ ##
2
+ #
3
+ # Makes a small pie graph suitable for display at 200px or even smaller.
4
+ #
5
+ module Gruffy
6
+ module Mini
7
+
8
+ class Pie < Gruffy::Pie
9
+
10
+ include Gruffy::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 Gruffy
6
+ module Mini
7
+
8
+ class SideBar < Gruffy::SideBar
9
+
10
+ include Gruffy::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
@@ -0,0 +1,127 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ # Experimental!!! See also the Spider graph.
4
+ class Gruffy::Net < Gruffy::Base
5
+
6
+ # Hide parts of the graph to fit more datapoints, or for a different appearance.
7
+ attr_accessor :hide_dots
8
+
9
+ # Dimensions of lines and dots; calculated based on dataset size if left unspecified
10
+ attr_accessor :line_width
11
+ attr_accessor :dot_radius
12
+
13
+ def initialize(*args)
14
+ super
15
+
16
+ @hide_dots = false
17
+ @hide_line_numbers = true
18
+ @sorted_drawing = true
19
+ end
20
+
21
+ def draw
22
+ super
23
+
24
+ return unless @has_data
25
+
26
+ @radius = @graph_height / 2.0
27
+ @center_x = @graph_left + (@graph_width / 2.0)
28
+ @center_y = @graph_top + (@graph_height / 2.0) - 10 # Move graph up a bit
29
+
30
+ @x_increment = @graph_width / (@column_count - 1).to_f
31
+ circle_radius = dot_radius ||
32
+ clip_value_if_greater_than(@columns / (@norm_data.first[DATA_VALUES_INDEX].size * 2.5), 5.0)
33
+
34
+ @d = @d.stroke_opacity 1.0
35
+ @d = @d.stroke_width line_width ||
36
+ clip_value_if_greater_than(@columns / (@norm_data.first[DATA_VALUES_INDEX].size * 4), 5.0)
37
+
38
+ if defined?(@norm_baseline)
39
+ level = @graph_top + (@graph_height - @norm_baseline * @graph_height)
40
+ @d = @d.push
41
+ @d.stroke_color @baseline_color
42
+ @d.fill_opacity 0.0
43
+ @d.stroke_dasharray(10, 20)
44
+ @d.stroke_width 5
45
+ @d.line(@graph_left, level, @graph_left + @graph_width, level)
46
+ @d = @d.pop
47
+ end
48
+
49
+ @norm_data.each do |data_row|
50
+ @d = @d.stroke data_row[DATA_COLOR_INDEX]
51
+ @d = @d.fill data_row[DATA_COLOR_INDEX]
52
+
53
+ data_row[DATA_VALUES_INDEX].each_with_index do |data_point, index|
54
+ next if data_point.nil?
55
+
56
+ rad_pos = index * Math::PI * 2 / @column_count
57
+ point_distance = data_point * @radius
58
+ start_x = @center_x + Math::sin(rad_pos) * point_distance
59
+ start_y = @center_y - Math::cos(rad_pos) * point_distance
60
+
61
+ next_index = index + 1 < data_row[DATA_VALUES_INDEX].length ? index + 1 : 0
62
+
63
+ next_rad_pos = next_index * Math::PI * 2 / @column_count
64
+ next_point_distance = data_row[DATA_VALUES_INDEX][next_index] * @radius
65
+ end_x = @center_x + Math::sin(next_rad_pos) * next_point_distance
66
+ end_y = @center_y - Math::cos(next_rad_pos) * next_point_distance
67
+
68
+ @d = @d.line(start_x, start_y, end_x, end_y)
69
+
70
+ @d = @d.circle(start_x, start_y, start_x - circle_radius, start_y) unless @hide_dots
71
+ end
72
+
73
+ end
74
+
75
+ @d.draw(@base_image)
76
+ end
77
+
78
+
79
+ # the lines connecting in the center, with the first line vertical
80
+ def draw_line_markers
81
+ return if @hide_line_markers
82
+
83
+
84
+ # have to do this here (AGAIN)... see draw() in this class
85
+ # because this funtion is called before the @radius, @center_x and @center_y are set
86
+ @radius = @graph_height / 2.0
87
+ @center_x = @graph_left + (@graph_width / 2.0)
88
+ @center_y = @graph_top + (@graph_height / 2.0) - 10 # Move graph up a bit
89
+
90
+
91
+ # Draw horizontal line markers and annotate with numbers
92
+ @d = @d.stroke(@marker_color)
93
+ @d = @d.stroke_width 1
94
+
95
+
96
+ (0..@column_count-1).each do |index|
97
+ rad_pos = index * Math::PI * 2 / @column_count
98
+
99
+ @d = @d.line(@center_x, @center_y, @center_x + Math::sin(rad_pos) * @radius, @center_y - Math::cos(rad_pos) * @radius)
100
+
101
+
102
+ marker_label = labels[index] ? labels[index].to_s : '000'
103
+
104
+ draw_label(@center_x, @center_y, rad_pos * 360 / (2 * Math::PI), @radius, marker_label)
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ def draw_label(center_x, center_y, angle, radius, amount)
111
+ r_offset = 1.1
112
+ x_offset = center_x # + 15 # The label points need to be tweaked slightly
113
+ y_offset = center_y # + 0 # This one doesn't though
114
+ x = x_offset + (radius * r_offset * Math.sin(deg2rad(angle)))
115
+ y = y_offset - (radius * r_offset * Math.cos(deg2rad(angle)))
116
+
117
+ # Draw label
118
+ @d.fill = @marker_color
119
+ @d.font = @font if @font
120
+ @d.pointsize = scale_fontsize(20)
121
+ @d.stroke = 'transparent'
122
+ @d.font_weight = BoldWeight
123
+ @d.gravity = CenterGravity
124
+ @d.annotate_scaled(@base_image, 0, 0, x, y, amount, @scale)
125
+ end
126
+
127
+ end
@@ -0,0 +1,100 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ # EXPERIMENTAL!
4
+ #
5
+ # Doesn't work yet.
6
+ #
7
+ class Gruffy::PhotoBar < Gruffy::Base
8
+
9
+ # TODO
10
+ #
11
+ # define base and cap in yml
12
+ # allow for image directory to be located elsewhere
13
+ # more exact measurements for bar heights (go all the way to the bottom of the graph)
14
+ # option to tile images instead of use a single image
15
+ # drop base label a few px lower so photo bar graphs can have a base dropping over the lower marker line
16
+ #
17
+
18
+ # The name of a pre-packaged photo-based theme.
19
+ attr_reader :theme
20
+
21
+ # def initialize(target_width=800)
22
+ # super
23
+ # init_photo_bar_graphics()
24
+ # end
25
+
26
+ def draw
27
+ super
28
+ return unless @has_data
29
+
30
+ return # TODO Remove for further development
31
+
32
+ init_photo_bar_graphics()
33
+
34
+ #Draw#define_clip_path()
35
+ #Draw#clip_path(pathname)
36
+ #Draw#composite....with bar graph image OverCompositeOp
37
+ #
38
+ # See also
39
+ #
40
+ # Draw.pattern # define an image to tile as the filling of a draw object
41
+ #
42
+
43
+ # Setup spacing.
44
+ #
45
+ # Columns sit side-by-side.
46
+ spacing_factor = 0.9
47
+ @bar_width = @norm_data[0][DATA_COLOR_INDEX].columns
48
+
49
+ @norm_data.each_with_index do |data_row, row_index|
50
+
51
+ data_row[DATA_VALUES_INDEX].each_with_index do |data_point, point_index|
52
+ data_point = 0 if data_point.nil?
53
+ # Use incremented x and scaled y
54
+ left_x = @graph_left + (@bar_width * (row_index + point_index + ((@data.length - 1) * point_index)))
55
+ left_y = @graph_top + (@graph_height - data_point * @graph_height) + 1
56
+ right_x = left_x + @bar_width * spacing_factor
57
+ right_y = @graph_top + @graph_height - 1
58
+
59
+ bar_image_width = data_row[DATA_COLOR_INDEX].columns
60
+ bar_image_height = right_y.to_f - left_y.to_f
61
+
62
+ # Crop to scale for data
63
+ bar_image = data_row[DATA_COLOR_INDEX].crop(0, 0, bar_image_width, bar_image_height)
64
+
65
+ @d.gravity = NorthWestGravity
66
+ @d = @d.composite(left_x, left_y, bar_image_width, bar_image_height, bar_image)
67
+
68
+ # Calculate center based on bar_width and current row
69
+ label_center = @graph_left + (@data.length * @bar_width * point_index) + (@data.length * @bar_width / 2.0)
70
+ draw_label(label_center, point_index)
71
+ end
72
+
73
+ end
74
+
75
+ @d.draw(@base_image)
76
+ end
77
+
78
+
79
+ # Return the chosen theme or the default
80
+ def theme
81
+ @theme || 'plastik'
82
+ end
83
+
84
+ protected
85
+
86
+ # Sets up colors with a list of images that will be used.
87
+ # Images should be 340px tall
88
+ def init_photo_bar_graphics
89
+ color_list = Array.new
90
+ theme_dir = File.dirname(__FILE__) + '/../../assets/' + theme
91
+
92
+ Dir.open(theme_dir).each do |file|
93
+ next unless /\.png$/.match(file)
94
+ color_list << Image.read("#{theme_dir}/#{file}").first
95
+ end
96
+ @colors = color_list
97
+ end
98
+
99
+ end
100
+