gruff 0.1.1 → 0.1.2
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/CHANGELOG +9 -1
- data/Rakefile +11 -1
- data/lib/gruff.rb +4 -0
- data/lib/gruff/bar.rb +0 -1
- data/lib/gruff/base.rb +162 -12
- data/lib/gruff/line.rb +3 -4
- data/lib/gruff/net.rb +133 -0
- data/lib/gruff/pie.rb +8 -46
- data/lib/gruff/scene.rb +196 -0
- data/lib/gruff/spider.rb +130 -0
- data/test/area_test.rb +2 -6
- data/test/bar_test.rb +61 -41
- data/test/base_test.rb +8 -0
- data/test/gruff_test_case.rb +13 -0
- data/test/legend_test.rb +71 -0
- data/test/line_test.rb +13 -54
- data/test/net_test.rb +230 -0
- data/test/photo_test.rb +2 -5
- data/test/pie_test.rb +2 -6
- data/test/scene_test.rb +94 -0
- data/test/sidestacked_bar_test.rb +2 -6
- data/test/spider_test.rb +216 -0
- data/test/stacked_bar_test.rb +2 -6
- metadata +54 -37
data/lib/gruff/pie.rb
CHANGED
@@ -11,7 +11,7 @@ class Gruff::Pie < Gruff::Base
|
|
11
11
|
return unless @has_data
|
12
12
|
|
13
13
|
diameter = @graph_height
|
14
|
-
radius = @graph_height / 2.0
|
14
|
+
radius = [@graph_width, @graph_height].min / 2.0
|
15
15
|
top_x = @graph_left + (@graph_width - diameter) / 2.0
|
16
16
|
center_x = @graph_left + (@graph_width / 2.0)
|
17
17
|
center_y = @graph_top + (@graph_height / 2.0) - 10 # Move graph up a bit
|
@@ -23,12 +23,15 @@ class Gruff::Pie < Gruff::Base
|
|
23
23
|
if data_row[1][0] > 0
|
24
24
|
@d = @d.stroke data_row[DATA_COLOR_INDEX]
|
25
25
|
@d = @d.fill 'transparent'
|
26
|
-
@d.stroke_width(
|
26
|
+
@d.stroke_width(radius) # stroke width should be equal to radius. we'll draw centered on (radius / 2)
|
27
27
|
|
28
28
|
current_degrees = (data_row[1][0] / total_sum) * 360.0
|
29
|
-
|
29
|
+
|
30
|
+
# ellipse will draw the the stroke centered on the first two parameters offset by the second two.
|
31
|
+
# therefore, in order to draw a circle of the proper diameter we must center the stroke at
|
32
|
+
# half the radius for both x and y
|
30
33
|
@d = @d.ellipse(center_x, center_y,
|
31
|
-
radius, radius,
|
34
|
+
radius / 2.0, radius / 2.0,
|
32
35
|
prev_degrees, prev_degrees + current_degrees + 0.5) # <= +0.5 'fudge factor' gets rid of the ugly gaps
|
33
36
|
|
34
37
|
half_angle = prev_degrees + ((prev_degrees + current_degrees) - prev_degrees) / 2
|
@@ -49,7 +52,7 @@ class Gruff::Pie < Gruff::Base
|
|
49
52
|
private
|
50
53
|
|
51
54
|
def draw_label(center_x, center_y, angle, radius, amount)
|
52
|
-
r_offset =
|
55
|
+
r_offset = 30 # The distance out from the center of the pie to get point
|
53
56
|
x_offset = center_x + 15 # The label points need to be tweaked slightly
|
54
57
|
y_offset = center_y + 0 # This one doesn't though
|
55
58
|
x = x_offset + ((radius + r_offset) * Math.cos(angle.deg2rad))
|
@@ -83,44 +86,3 @@ class Float
|
|
83
86
|
self * (Math::PI/180.0)
|
84
87
|
end
|
85
88
|
end
|
86
|
-
|
87
|
-
# From sparklines...not currently used
|
88
|
-
class Magick::Draw
|
89
|
-
|
90
|
-
def pie_slice(center_x=0.0, center_y=0.0, radius=100.0, percent=0.0, rot_x=0.0)
|
91
|
-
# Okay, this part is as confusing as hell, so pay attention:
|
92
|
-
# This line determines the horizontal portion of the point on the circle where the X-Axis
|
93
|
-
# should end. It's caculated by taking the center of the on-image circle and adding that
|
94
|
-
# to the radius multiplied by the formula for determinig the point on a unit circle that a
|
95
|
-
# angle corresponds to. 3.6 * percent gives us that angle, but it's in degrees, so we need to
|
96
|
-
# convert, hence the muliplication by Pi over 180
|
97
|
-
arc_end_x = radius + (radius * Math.cos((3.6 * percent)*(Math::PI/180.0)))
|
98
|
-
|
99
|
-
# The same goes for here, except it's the vertical point instead of the horizontal one
|
100
|
-
arc_end_y = radius + (radius * Math.sin((3.6 * percent)*(Math::PI/180.0)))
|
101
|
-
|
102
|
-
# Because the SVG path format is seriously screwy, we need to set the large-arc-flag to 1
|
103
|
-
# if the angle of an arc is greater than 180 degrees. I have no idea why this is, but it is.
|
104
|
-
percent > 50 ? large_arc_flag = 1 : large_arc_flag = 0
|
105
|
-
|
106
|
-
# This is also confusing
|
107
|
-
# M tells us to move to an absolute point on the image.
|
108
|
-
# We're moving to the center of the pie
|
109
|
-
# h tells us to move to a relative point.
|
110
|
-
# We're moving to the right edge of the circle.
|
111
|
-
# A tells us to start an absolute elliptical arc.
|
112
|
-
# The first two values are the radii of the ellipse
|
113
|
-
# The third value is the x-axis-rotation (how to rotate the ellipse)
|
114
|
-
# The fourth value is our large-arc-flag
|
115
|
-
# The fifth is the sweep-flag
|
116
|
-
# The sixth and seventh values are the end point of the arc which we calculated previously
|
117
|
-
# More info on the SVG path string format at: http://www.w3.org/TR/SVG/paths.html
|
118
|
-
#
|
119
|
-
#path = "M#{radius + 2},#{radius + 2} h#{radius} A#{radius},#{radius} #{rot_x} #{large_arc_flag},1 #{arc_end_x},#{arc_end_y} z"
|
120
|
-
path = "M#{radius},#{radius} h#{radius} A#{radius},#{radius} #{rot_x} #{large_arc_flag},1 #{arc_end_x},#{arc_end_y} z"
|
121
|
-
puts "PATH: #{path}"
|
122
|
-
|
123
|
-
self.path(path)
|
124
|
-
end
|
125
|
-
|
126
|
-
end
|
data/lib/gruff/scene.rb
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
|
2
|
+
require "observer"
|
3
|
+
require File.dirname(__FILE__) + '/base'
|
4
|
+
|
5
|
+
# EXPERIMENTAL!
|
6
|
+
#
|
7
|
+
# Started by Geoffrey Grosenbach at Canada on Rails, April 2006.
|
8
|
+
#
|
9
|
+
# A scene is a non-linear graph that assembles layers together to tell a story.
|
10
|
+
# Layers are folders with appropriately named files (see below). You can group
|
11
|
+
# layers and control them together or just set their values individually.
|
12
|
+
#
|
13
|
+
# Examples:
|
14
|
+
#
|
15
|
+
# * A city scene that changes with the time of day and the weather conditions.
|
16
|
+
# * A traffic map that shows red lines on streets that are crowded and green on free-flowing ones.
|
17
|
+
#
|
18
|
+
# Usage:
|
19
|
+
#
|
20
|
+
# g = Gruff::Scene.new("500x100", "artwork/city_scene")
|
21
|
+
# g.layers = %w(background haze sky clouds)
|
22
|
+
# g.weather_group = %w(clouds)
|
23
|
+
# g.time_group = %w(background sky)
|
24
|
+
# g.weather = "cloudy"
|
25
|
+
# g.time = Time.now
|
26
|
+
# g.haze = true
|
27
|
+
# g.write "hazy_daytime_city_scene.png"
|
28
|
+
#
|
29
|
+
#
|
30
|
+
#
|
31
|
+
# If there is a file named 'default.png', it will be selected (unless other values are provided to override it).
|
32
|
+
#
|
33
|
+
class Gruff::Scene < Gruff::Base
|
34
|
+
|
35
|
+
# An array listing the foldernames that will be rendered, from back to front.
|
36
|
+
#
|
37
|
+
# g.layers = %w(sky clouds buildings street people)
|
38
|
+
#
|
39
|
+
attr_accessor :layers
|
40
|
+
|
41
|
+
def initialize(target_width, base_dir)
|
42
|
+
@base_dir = base_dir
|
43
|
+
@groups = {}
|
44
|
+
@layers = []
|
45
|
+
super target_width
|
46
|
+
end
|
47
|
+
|
48
|
+
def draw
|
49
|
+
# Join all the custom paths and filter out the empty ones
|
50
|
+
image_paths = @layers.map { |layer| layer.path }.select { |path| !path.empty? }
|
51
|
+
images = Magick::ImageList.new(*image_paths)
|
52
|
+
@base_image = images.flatten_images
|
53
|
+
end
|
54
|
+
|
55
|
+
def layers=(ordered_list)
|
56
|
+
ordered_list.each do |layer_name|
|
57
|
+
@layers << Gruff::Layer.new(@base_dir, layer_name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Group layers to input values
|
62
|
+
#
|
63
|
+
# g.weather_group = ["sky", "sea", "clouds"]
|
64
|
+
#
|
65
|
+
# Set input values
|
66
|
+
#
|
67
|
+
# g.weather = "cloudy"
|
68
|
+
#
|
69
|
+
def method_missing(method_name, *args)
|
70
|
+
case method_name.to_s
|
71
|
+
when /^(\w+)_group=$/
|
72
|
+
add_group $1, *args
|
73
|
+
return
|
74
|
+
when /^(\w+)=$/
|
75
|
+
set_input $1, args.first
|
76
|
+
return
|
77
|
+
end
|
78
|
+
super
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def add_group(input_name, layer_names)
|
84
|
+
@groups[input_name] = Gruff::Group.new(input_name, @layers.select { |layer| layer_names.include?(layer.name) })
|
85
|
+
end
|
86
|
+
|
87
|
+
def set_input(input_name, input_value)
|
88
|
+
if not @groups[input_name].nil?
|
89
|
+
@groups[input_name].send_updates(input_value)
|
90
|
+
else
|
91
|
+
if chosen_layer = @layers.detect { |layer| layer.name == input_name }
|
92
|
+
chosen_layer.update input_value
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
class Gruff::Group
|
101
|
+
|
102
|
+
include Observable
|
103
|
+
attr_reader :name
|
104
|
+
|
105
|
+
def initialize(folder_name, layers)
|
106
|
+
@name = folder_name
|
107
|
+
layers.each do |layer|
|
108
|
+
layer.observe self
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def send_updates(value)
|
113
|
+
changed
|
114
|
+
notify_observers value
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
class Gruff::Layer
|
121
|
+
|
122
|
+
attr_reader :name
|
123
|
+
|
124
|
+
def initialize(base_dir, folder_name)
|
125
|
+
@base_dir = base_dir.to_s
|
126
|
+
@name = folder_name.to_s
|
127
|
+
@filenames = Dir.open(File.join(base_dir, folder_name)).entries.select { |file| file =~ /^[^.]+\.png$/ }
|
128
|
+
@selected_filename = select_default
|
129
|
+
end
|
130
|
+
|
131
|
+
# Register this layer so it receives updates from the group
|
132
|
+
def observe(obj)
|
133
|
+
obj.add_observer self
|
134
|
+
end
|
135
|
+
|
136
|
+
# Choose the appropriate filename for this layer, based on the input
|
137
|
+
def update(value)
|
138
|
+
@selected_filename = case value.to_s
|
139
|
+
when /^(true|false)$/
|
140
|
+
select_boolean value
|
141
|
+
when /^(\w|\s)+$/
|
142
|
+
select_string value
|
143
|
+
when /^-?(\d+\.)?\d+$/
|
144
|
+
select_numeric value
|
145
|
+
when /(\d\d):(\d\d):\d\d/
|
146
|
+
select_time "#{$1}#{$2}"
|
147
|
+
else
|
148
|
+
select_default
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Returns the full path to the selected image, or a blank string
|
153
|
+
def path
|
154
|
+
unless @selected_filename.nil? || @selected_filename.empty?
|
155
|
+
return File.join(@base_dir, @name, @selected_filename)
|
156
|
+
end
|
157
|
+
''
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
# Match "true.png" or "false.png"
|
163
|
+
def select_boolean(value)
|
164
|
+
file_exists_or_blank value.to_s
|
165
|
+
end
|
166
|
+
|
167
|
+
# Match -5 to _5.png
|
168
|
+
def select_numeric(value)
|
169
|
+
file_exists_or_blank value.to_s.gsub('-', '_')
|
170
|
+
end
|
171
|
+
|
172
|
+
def select_time(value)
|
173
|
+
times = @filenames.map { |filename| filename.gsub('.png', '') }
|
174
|
+
times.each_with_index do |time, index|
|
175
|
+
if (time > value) && (index > 0)
|
176
|
+
return "#{times[index - 1]}.png"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
return "#{times.last}.png"
|
180
|
+
end
|
181
|
+
|
182
|
+
# Match "partly cloudy" to "partly_cloudy.png"
|
183
|
+
def select_string(value)
|
184
|
+
file_exists_or_blank value.to_s.gsub(' ', '_')
|
185
|
+
end
|
186
|
+
|
187
|
+
def select_default
|
188
|
+
file_exists_or_blank "default"
|
189
|
+
end
|
190
|
+
|
191
|
+
# Returns the string "#{filename}.png", if it exists
|
192
|
+
def file_exists_or_blank(filename)
|
193
|
+
@filenames.include?("#{filename}.png") ? "#{filename}.png" : ''
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
data/lib/gruff/spider.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
|
2
|
+
require File.dirname(__FILE__) + '/base'
|
3
|
+
|
4
|
+
# Experimental!!! See also the Net graph.
|
5
|
+
#
|
6
|
+
# Submitted by Kevin Clark http://glu.ttono.us/
|
7
|
+
class Gruff::Spider < Gruff::Base
|
8
|
+
|
9
|
+
# Hide all text
|
10
|
+
attr_accessor :hide_text
|
11
|
+
attr_accessor :hide_axes
|
12
|
+
attr_accessor :transparent_background
|
13
|
+
|
14
|
+
def transparent_background=(value)
|
15
|
+
@transparent_background = value
|
16
|
+
@base_image = render_transparent_background if value
|
17
|
+
end
|
18
|
+
|
19
|
+
def hide_text=(value)
|
20
|
+
@hide_title = @hide_text = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(max_value, target_width = 800)
|
24
|
+
super(target_width)
|
25
|
+
@max_value = max_value
|
26
|
+
@hide_legend = true;
|
27
|
+
end
|
28
|
+
|
29
|
+
def draw
|
30
|
+
@hide_line_markers = true
|
31
|
+
|
32
|
+
super
|
33
|
+
|
34
|
+
return unless @has_data
|
35
|
+
|
36
|
+
# Setup basic positioning
|
37
|
+
diameter = @graph_height
|
38
|
+
radius = @graph_height / 2.0
|
39
|
+
top_x = @graph_left + (@graph_width - diameter) / 2.0
|
40
|
+
center_x = @graph_left + (@graph_width / 2.0)
|
41
|
+
center_y = @graph_top + (@graph_height / 2.0) - 25 # Move graph up a bit
|
42
|
+
|
43
|
+
@unit_length = radius / @max_value
|
44
|
+
|
45
|
+
|
46
|
+
total_sum = sums_for_spider
|
47
|
+
prev_degrees = 0.0
|
48
|
+
additive_angle = (2 * Math::PI)/ @data.size
|
49
|
+
|
50
|
+
current_angle = 0.0
|
51
|
+
|
52
|
+
# Draw axes
|
53
|
+
draw_axes(center_x, center_y, radius, additive_angle) unless hide_axes
|
54
|
+
|
55
|
+
# Draw polygon
|
56
|
+
draw_polygon(center_x, center_y, additive_angle)
|
57
|
+
|
58
|
+
|
59
|
+
@d.draw(@base_image)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def normalize_points(value)
|
65
|
+
value * @unit_length
|
66
|
+
end
|
67
|
+
|
68
|
+
def draw_label(center_x, center_y, angle, radius, amount)
|
69
|
+
r_offset = 50 # The distance out from the center of the pie to get point
|
70
|
+
x_offset = center_x # The label points need to be tweaked slightly
|
71
|
+
y_offset = center_y + 0 # This one doesn't though
|
72
|
+
x = x_offset + ((radius + r_offset) * Math.cos(angle))
|
73
|
+
y = y_offset + ((radius + r_offset) * Math.sin(angle))
|
74
|
+
|
75
|
+
# Draw label
|
76
|
+
@d.fill = @marker_color
|
77
|
+
@d.font = @font if @font
|
78
|
+
@d.pointsize = scale_fontsize(legend_font_size)
|
79
|
+
@d.stroke = 'transparent'
|
80
|
+
@d.font_weight = BoldWeight
|
81
|
+
@d.gravity = CenterGravity
|
82
|
+
@d.annotate_scaled( @base_image,
|
83
|
+
0, 0,
|
84
|
+
x, y,
|
85
|
+
amount, @scale)
|
86
|
+
end
|
87
|
+
|
88
|
+
def draw_axes(center_x, center_y, radius, additive_angle, line_color = nil)
|
89
|
+
return if hide_axes
|
90
|
+
|
91
|
+
current_angle = 0.0
|
92
|
+
|
93
|
+
@data.each do |data_row|
|
94
|
+
@d.stroke(line_color || data_row[DATA_COLOR_INDEX])
|
95
|
+
@d.stroke_width 5.0
|
96
|
+
|
97
|
+
x_offset = radius * Math.cos(current_angle)
|
98
|
+
y_offset = radius * Math.sin(current_angle)
|
99
|
+
|
100
|
+
@d.line(center_x, center_y,
|
101
|
+
center_x + x_offset,
|
102
|
+
center_y + y_offset)
|
103
|
+
|
104
|
+
draw_label(center_x, center_y, current_angle, radius, data_row[0].to_s) unless hide_text
|
105
|
+
|
106
|
+
current_angle += additive_angle
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def draw_polygon(center_x, center_y, additive_angle, color = nil)
|
111
|
+
points = []
|
112
|
+
current_angle = 0.0
|
113
|
+
@data.each do |data_row|
|
114
|
+
points << center_x + normalize_points(data_row[1][0]) * Math.cos(current_angle)
|
115
|
+
points << center_y + normalize_points(data_row[1][0]) * Math.sin(current_angle)
|
116
|
+
current_angle += additive_angle
|
117
|
+
end
|
118
|
+
|
119
|
+
@d.stroke_width 1.0
|
120
|
+
@d.stroke(color || @marker_color)
|
121
|
+
@d.fill(color || @marker_color)
|
122
|
+
@d.fill_opacity 0.4
|
123
|
+
@d.polygon(*points)
|
124
|
+
end
|
125
|
+
|
126
|
+
def sums_for_spider
|
127
|
+
@data.inject(0.0) {|sum, data_row| sum += data_row[1][0]}
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
data/test/area_test.rb
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
2
|
|
3
|
-
|
4
|
-
#$:.unshift File.dirname(__FILE__) + "/fixtures/helpers"
|
3
|
+
require File.dirname(__FILE__) + "/gruff_test_case"
|
5
4
|
|
6
|
-
|
7
|
-
require 'gruff'
|
8
|
-
|
9
|
-
class TestGruffArea < Test::Unit::TestCase
|
5
|
+
class TestGruffArea < GruffTestCase
|
10
6
|
|
11
7
|
def setup
|
12
8
|
@datasets = [
|
data/test/bar_test.rb
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
2
|
|
3
|
-
|
4
|
-
#$:.unshift File.dirname(__FILE__) + "/fixtures/helpers"
|
3
|
+
require File.dirname(__FILE__) + "/gruff_test_case"
|
5
4
|
|
6
|
-
|
7
|
-
require 'gruff'
|
8
|
-
|
9
|
-
class TestGruffBar < Test::Unit::TestCase
|
5
|
+
class TestGruffBar < GruffTestCase
|
10
6
|
|
11
7
|
# TODO Delete old output files once when starting tests
|
12
8
|
|
@@ -76,6 +72,8 @@ class TestGruffBar < Test::Unit::TestCase
|
|
76
72
|
g.data(:Art, [0, 5, 8, 15], '#990000')
|
77
73
|
g.data(:Philosophy, [10, 3, 2, 8], '#009900')
|
78
74
|
g.data(:Science, [2, 15, 8, 11], '#990099')
|
75
|
+
|
76
|
+
g.minimum_value = 0
|
79
77
|
|
80
78
|
g.write("test/output/bar_manual_colors.png")
|
81
79
|
end
|
@@ -96,22 +94,6 @@ class TestGruffBar < Test::Unit::TestCase
|
|
96
94
|
g.write("test/output/bar_keynote_small.png")
|
97
95
|
end
|
98
96
|
|
99
|
-
def test_bar_image_bg
|
100
|
-
g = setup_basic_graph()
|
101
|
-
g.title = "With Image Background"
|
102
|
-
g.theme_image_based
|
103
|
-
g.write("test/output/bar_image.png")
|
104
|
-
|
105
|
-
g = setup_basic_graph(400)
|
106
|
-
g.title = "With Image Background Small"
|
107
|
-
g.theme_image_based
|
108
|
-
g.write("test/output/bar_image_small.png")
|
109
|
-
|
110
|
-
g = setup_basic_graph('800x400')
|
111
|
-
g.title = "With Image Background Small"
|
112
|
-
g.theme_image_based
|
113
|
-
g.write("test/output/bar_image_wide.png")
|
114
|
-
end
|
115
97
|
|
116
98
|
def test_nil_font
|
117
99
|
g = setup_basic_graph 400
|
@@ -139,6 +121,19 @@ class TestGruffBar < Test::Unit::TestCase
|
|
139
121
|
end
|
140
122
|
|
141
123
|
|
124
|
+
def test_one_value
|
125
|
+
g = Gruff::Bar.new
|
126
|
+
g.title = "One Value Graph Test"
|
127
|
+
g.labels = {
|
128
|
+
0 => '1',
|
129
|
+
1 => '2'
|
130
|
+
}
|
131
|
+
g.data('one', [1,1])
|
132
|
+
|
133
|
+
g.write("test/output/bar_one_value.png")
|
134
|
+
end
|
135
|
+
|
136
|
+
|
142
137
|
def test_negative
|
143
138
|
g = Gruff::Bar.new
|
144
139
|
g.title = "Pos/Neg Bar Graph Test"
|
@@ -155,6 +150,50 @@ class TestGruffBar < Test::Unit::TestCase
|
|
155
150
|
end
|
156
151
|
|
157
152
|
|
153
|
+
def test_nearly_zero
|
154
|
+
g = Gruff::Bar.new
|
155
|
+
g.title = "Nearly Zero Graph"
|
156
|
+
g.labels = {
|
157
|
+
0 => '5/6',
|
158
|
+
1 => '5/15',
|
159
|
+
2 => '5/24',
|
160
|
+
3 => '5/30',
|
161
|
+
}
|
162
|
+
g.data(:apples, [1, 2, 3, 4])
|
163
|
+
g.data(:peaches, [4, 3, 2, 1])
|
164
|
+
g.minimum_value = 0
|
165
|
+
g.maximum_value = 10
|
166
|
+
g.write("test/output/bar_nearly_zero.png")
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
def test_custom_theme
|
171
|
+
g = Gruff::Bar.new
|
172
|
+
g.title = "Custom Theme"
|
173
|
+
g.title_font_size = 60
|
174
|
+
g.legend_font_size = 32
|
175
|
+
g.marker_font_size = 32
|
176
|
+
g.theme = {
|
177
|
+
:colors => %w(#efd250 #666699 #e5573f #9595e2),
|
178
|
+
:marker_color => 'white',
|
179
|
+
:background_image => "assets/pc306715.jpg"
|
180
|
+
}
|
181
|
+
g.labels = {
|
182
|
+
0 => '5/6',
|
183
|
+
1 => '5/15',
|
184
|
+
2 => '5/24',
|
185
|
+
3 => '5/30',
|
186
|
+
}
|
187
|
+
g.data(:vancouver, [1, 2, 3, 4])
|
188
|
+
g.data(:seattle, [2, 4, 6, 8])
|
189
|
+
g.data(:portland, [3, 1, 7, 3])
|
190
|
+
g.data(:victoria, [4, 3, 5, 7])
|
191
|
+
g.minimum_value = 0
|
192
|
+
g.write("test/output/bar_themed.png")
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
|
158
197
|
protected
|
159
198
|
|
160
199
|
def setup_basic_graph(size=800)
|
@@ -175,22 +214,3 @@ protected
|
|
175
214
|
|
176
215
|
end
|
177
216
|
|
178
|
-
class Gruff::Base
|
179
|
-
# A color scheme from the colors used on the 2005 Rails keynote presentation at RubyConf.
|
180
|
-
def theme_image_based
|
181
|
-
reset_themes()
|
182
|
-
# Colors
|
183
|
-
@green = '#00ff00'
|
184
|
-
@grey = '#333333'
|
185
|
-
@orange = '#ff5d00'
|
186
|
-
@red = '#f61100'
|
187
|
-
@white = 'white'
|
188
|
-
@light_grey = '#999999'
|
189
|
-
@black = 'black'
|
190
|
-
@colors = [@green, @grey, @orange, @red, @white, @light_grey, @black]
|
191
|
-
|
192
|
-
@marker_color = 'white'
|
193
|
-
|
194
|
-
@base_image = render_image_background('assets/pc306715.jpg')
|
195
|
-
end
|
196
|
-
end
|