gruffy 0.0.2 → 0.0.3

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.
@@ -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
+