gruff 0.6.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +5 -5
  2. data/.editorconfig +14 -0
  3. data/.github/ISSUE_TEMPLATE.md +18 -0
  4. data/.gitignore +3 -0
  5. data/.rubocop.yml +109 -0
  6. data/.rubocop_todo.yml +112 -0
  7. data/.travis.yml +24 -15
  8. data/.yardopts +1 -0
  9. data/{History.txt → CHANGELOG.md} +72 -25
  10. data/Gemfile +3 -7
  11. data/README.md +57 -25
  12. data/Rakefile +21 -192
  13. data/assets/plastik/blue.png +0 -0
  14. data/assets/plastik/green.png +0 -0
  15. data/assets/plastik/red.png +0 -0
  16. data/docker/Dockerfile +14 -0
  17. data/docker/build.sh +4 -0
  18. data/docker/launch.sh +4 -0
  19. data/gruff.gemspec +21 -13
  20. data/init.rb +2 -0
  21. data/lib/gruff.rb +26 -2
  22. data/lib/gruff/accumulator_bar.rb +18 -8
  23. data/lib/gruff/area.rb +33 -19
  24. data/lib/gruff/bar.rb +76 -45
  25. data/lib/gruff/base.rb +435 -704
  26. data/lib/gruff/bezier.rb +32 -17
  27. data/lib/gruff/bullet.rb +62 -68
  28. data/lib/gruff/dot.rb +38 -82
  29. data/lib/gruff/helper/bar_conversion.rb +47 -0
  30. data/lib/gruff/helper/bar_value_label_mixin.rb +30 -0
  31. data/lib/gruff/helper/stacked_mixin.rb +23 -0
  32. data/lib/gruff/histogram.rb +60 -0
  33. data/lib/gruff/line.rb +134 -170
  34. data/lib/gruff/mini/bar.rb +17 -10
  35. data/lib/gruff/mini/legend.rb +24 -36
  36. data/lib/gruff/mini/pie.rb +18 -12
  37. data/lib/gruff/mini/side_bar.rb +26 -12
  38. data/lib/gruff/net.rb +68 -81
  39. data/lib/gruff/patch/rmagick.rb +33 -0
  40. data/lib/gruff/patch/string.rb +10 -0
  41. data/lib/gruff/photo_bar.rb +39 -42
  42. data/lib/gruff/pie.rb +180 -89
  43. data/lib/gruff/renderer/bezier.rb +21 -0
  44. data/lib/gruff/renderer/circle.rb +21 -0
  45. data/lib/gruff/renderer/dash_line.rb +22 -0
  46. data/lib/gruff/renderer/dot.rb +39 -0
  47. data/lib/gruff/renderer/ellipse.rb +21 -0
  48. data/lib/gruff/renderer/line.rb +42 -0
  49. data/lib/gruff/renderer/polygon.rb +23 -0
  50. data/lib/gruff/renderer/polyline.rb +21 -0
  51. data/lib/gruff/renderer/rectangle.rb +19 -0
  52. data/lib/gruff/renderer/renderer.rb +132 -0
  53. data/lib/gruff/renderer/text.rb +53 -0
  54. data/lib/gruff/scatter.rb +163 -182
  55. data/lib/gruff/scene.rb +31 -41
  56. data/lib/gruff/side_bar.rb +81 -65
  57. data/lib/gruff/side_stacked_bar.rb +78 -62
  58. data/lib/gruff/spider.rb +49 -57
  59. data/lib/gruff/stacked_area.rb +40 -32
  60. data/lib/gruff/stacked_bar.rb +86 -53
  61. data/lib/gruff/store/base_data.rb +38 -0
  62. data/lib/gruff/store/custom_data.rb +38 -0
  63. data/lib/gruff/store/store.rb +80 -0
  64. data/lib/gruff/store/xy_data.rb +59 -0
  65. data/lib/gruff/themes.rb +32 -33
  66. data/lib/gruff/version.rb +3 -1
  67. metadata +80 -89
  68. data/Manifest.txt +0 -81
  69. data/RELEASE.md +0 -30
  70. data/assets/bubble.png +0 -0
  71. data/assets/city_scene/background/0000.png +0 -0
  72. data/assets/city_scene/background/0600.png +0 -0
  73. data/assets/city_scene/background/2000.png +0 -0
  74. data/assets/city_scene/clouds/cloudy.png +0 -0
  75. data/assets/city_scene/clouds/partly_cloudy.png +0 -0
  76. data/assets/city_scene/clouds/stormy.png +0 -0
  77. data/assets/city_scene/grass/default.png +0 -0
  78. data/assets/city_scene/haze/true.png +0 -0
  79. data/assets/city_scene/number_sample/1.png +0 -0
  80. data/assets/city_scene/number_sample/2.png +0 -0
  81. data/assets/city_scene/number_sample/default.png +0 -0
  82. data/assets/city_scene/sky/0000.png +0 -0
  83. data/assets/city_scene/sky/0200.png +0 -0
  84. data/assets/city_scene/sky/0400.png +0 -0
  85. data/assets/city_scene/sky/0600.png +0 -0
  86. data/assets/city_scene/sky/0800.png +0 -0
  87. data/assets/city_scene/sky/1000.png +0 -0
  88. data/assets/city_scene/sky/1200.png +0 -0
  89. data/assets/city_scene/sky/1400.png +0 -0
  90. data/assets/city_scene/sky/1500.png +0 -0
  91. data/assets/city_scene/sky/1700.png +0 -0
  92. data/assets/city_scene/sky/2000.png +0 -0
  93. data/assets/pc306715.jpg +0 -0
  94. data/lib/gruff/bar_conversion.rb +0 -46
  95. data/lib/gruff/deprecated.rb +0 -39
  96. data/lib/gruff/stacked_mixin.rb +0 -23
  97. data/test/gruff_test_case.rb +0 -154
  98. data/test/image_compare.rb +0 -58
  99. data/test/test_accumulator_bar.rb +0 -51
  100. data/test/test_area.rb +0 -134
  101. data/test/test_bar.rb +0 -505
  102. data/test/test_base.rb +0 -8
  103. data/test/test_bezier.rb +0 -33
  104. data/test/test_bullet.rb +0 -26
  105. data/test/test_dot.rb +0 -263
  106. data/test/test_labels_for_null_data.rb +0 -27
  107. data/test/test_legend.rb +0 -68
  108. data/test/test_line.rb +0 -657
  109. data/test/test_mini_bar.rb +0 -33
  110. data/test/test_mini_pie.rb +0 -25
  111. data/test/test_mini_side_bar.rb +0 -36
  112. data/test/test_net.rb +0 -231
  113. data/test/test_photo.rb +0 -41
  114. data/test/test_pie.rb +0 -161
  115. data/test/test_scatter.rb +0 -233
  116. data/test/test_scene.rb +0 -100
  117. data/test/test_side_bar.rb +0 -56
  118. data/test/test_sidestacked_bar.rb +0 -105
  119. data/test/test_spider.rb +0 -226
  120. data/test/test_stacked_area.rb +0 -52
  121. data/test/test_stacked_bar.rb +0 -68
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Magick
4
+ class Draw
5
+ # Additional method to scale annotation text since Draw.scale doesn't.
6
+ def annotate_scaled(img, width, height, x, y, text, scale)
7
+ scaled_width = (width * scale) >= 1 ? (width * scale) : 1
8
+ scaled_height = (height * scale) >= 1 ? (height * scale) : 1
9
+
10
+ annotate(img,
11
+ scaled_width, scaled_height,
12
+ x * scale, y * scale,
13
+ text.gsub('%', '%%'))
14
+ end
15
+
16
+ remove_method :stroke_opacity
17
+ def stroke_opacity(_opacity)
18
+ raise '#stroke_opacity method has different behavior between RMagick and RMagick4J. Should not use this method.'
19
+ end
20
+
21
+ if defined? JRUBY_VERSION
22
+ # FIXME(uwe): We should NOT need to implement this method.
23
+ # Remove this method as soon as RMagick4J Issue #16 is fixed.
24
+ # https://github.com/Serabe/RMagick4J/issues/16
25
+ def fill=(fill)
26
+ fill = { white: '#FFFFFF' }[fill.to_sym] || fill
27
+ @draw.fill = Magick4J.ColorDatabase.query_default(fill)
28
+ self
29
+ end
30
+ # EMXIF
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class String
4
+ THOUSAND_SEPARATOR = ','
5
+
6
+ #Taken from http://codesnippets.joyent.com/posts/show/330
7
+ def commify(delimiter = THOUSAND_SEPARATOR)
8
+ gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
9
+ end
10
+ end
@@ -1,36 +1,37 @@
1
- require File.dirname(__FILE__) + '/base'
1
+ # frozen_string_literal: true
2
+
3
+ require 'gruff/base'
2
4
 
3
5
  # EXPERIMENTAL!
4
6
  #
5
7
  # Doesn't work yet.
6
8
  #
7
9
  class Gruff::PhotoBar < Gruff::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
- #
10
+ # TODO
11
+ #
12
+ # define base and cap in yml
13
+ # allow for image directory to be located elsewhere
14
+ # more exact measurements for bar heights (go all the way to the bottom of the graph)
15
+ # option to tile images instead of use a single image
16
+ # drop base label a few px lower so photo bar graphs can have a base dropping over the lower marker line
17
+ #
17
18
 
18
19
  # The name of a pre-packaged photo-based theme.
19
20
  attr_reader :theme
20
21
 
21
- # def initialize(target_width=800)
22
- # super
23
- # init_photo_bar_graphics()
24
- # end
22
+ # def initialize(target_width=800)
23
+ # super
24
+ # init_photo_bar_graphics()
25
+ # end
25
26
 
26
27
  def draw
27
28
  super
28
- return unless @has_data
29
+ return unless data_given?
29
30
 
30
- return # TODO Remove for further development
31
+ return # TODO: Remove for further development
32
+
33
+ init_photo_bar_graphics
31
34
 
32
- init_photo_bar_graphics()
33
-
34
35
  #Draw#define_clip_path()
35
36
  #Draw#clip_path(pathname)
36
37
  #Draw#composite....with bar graph image OverCompositeOp
@@ -38,44 +39,41 @@ class Gruff::PhotoBar < Gruff::Base
38
39
  # See also
39
40
  #
40
41
  # Draw.pattern # define an image to tile as the filling of a draw object
41
- #
42
+ #
42
43
 
43
44
  # Setup spacing.
44
45
  #
45
46
  # Columns sit side-by-side.
46
47
  spacing_factor = 0.9
47
- @bar_width = @norm_data[0][DATA_COLOR_INDEX].columns
48
+ bar_width = store.norm_data[0].color.columns
48
49
 
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|
50
+ store.norm_data.each_with_index do |data_row, row_index|
51
+ data_row.points.each_with_index do |data_point, point_index|
52
52
  data_point = 0 if data_point.nil?
53
53
  # Use incremented x and scaled y
54
- left_x = @graph_left + (@bar_width * (row_index + point_index + ((@data.length - 1) * point_index)))
54
+ left_x = @graph_left + (bar_width * (row_index + point_index + ((store.length - 1) * point_index)))
55
55
  left_y = @graph_top + (@graph_height - data_point * @graph_height) + 1
56
- right_x = left_x + @bar_width * spacing_factor
56
+ right_x = left_x + bar_width * spacing_factor
57
57
  right_y = @graph_top + @graph_height - 1
58
-
59
- bar_image_width = data_row[DATA_COLOR_INDEX].columns
58
+
59
+ bar_image_width = data_row.color.columns
60
60
  bar_image_height = right_y.to_f - left_y.to_f
61
-
61
+
62
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
-
63
+ bar_image = data_row.color.crop(0, 0, bar_image_width, bar_image_height)
64
+
65
+ @d.gravity = Magick::NorthWestGravity
66
+ @d.composite(left_x, left_y, bar_image_width, bar_image_height, bar_image)
67
+
68
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)
69
+ label_center = @graph_left + (store.length * bar_width * point_index) + (store.length * bar_width / 2.0)
70
70
  draw_label(label_center, point_index)
71
71
  end
72
-
73
72
  end
74
73
 
75
- @d.draw(@base_image)
74
+ Gruff::Renderer.finish
76
75
  end
77
76
 
78
-
79
77
  # Return the chosen theme or the default
80
78
  def theme
81
79
  @theme || 'plastik'
@@ -85,16 +83,15 @@ protected
85
83
 
86
84
  # Sets up colors with a list of images that will be used.
87
85
  # Images should be 340px tall
88
- def init_photo_bar_graphics
89
- color_list = Array.new
86
+ def init_photo_bar_graphics
87
+ color_list = []
90
88
  theme_dir = File.dirname(__FILE__) + '/../../assets/' + theme
91
89
 
92
90
  Dir.open(theme_dir).each do |file|
93
91
  next unless /\.png$/.match(file)
94
- color_list << Image.read("#{theme_dir}/#{file}").first
92
+
93
+ color_list << Magick::Image.read("#{theme_dir}/#{file}").first
95
94
  end
96
95
  @colors = color_list
97
96
  end
98
-
99
97
  end
100
-
@@ -1,31 +1,40 @@
1
- require File.dirname(__FILE__) + '/base'
1
+ # frozen_string_literal: true
2
2
 
3
- ##
4
- # Here's how to make a Pie graph:
3
+ require 'gruff/base'
4
+
5
+ #
6
+ # Here's how to make a Gruff::Pie.
5
7
  #
6
8
  # g = Gruff::Pie.new
7
9
  # g.title = "Visual Pie Graph Test"
8
10
  # g.data 'Fries', 20
9
11
  # g.data 'Hamburgers', 50
10
- # g.write("test/output/pie_keynote.png")
12
+ # g.write("pie_keynote.png")
13
+ #
14
+ # To control where the pie chart starts creating slices, use {#zero_degree=}.
11
15
  #
12
- # To control where the pie chart starts creating slices, use #zero_degree.
13
-
14
16
  class Gruff::Pie < Gruff::Base
15
-
16
17
  DEFAULT_TEXT_OFFSET_PERCENTAGE = 0.15
17
18
 
18
19
  # Can be used to make the pie start cutting slices at the top (-90.0)
19
- # or at another angle. Default is 0.0, which starts at 3 o'clock.
20
- attr_accessor :zero_degree
20
+ # or at another angle. Default is +0.0+, which starts at 3 o'clock.
21
+ attr_writer :zero_degree
22
+
21
23
  # Do not show labels for slices that are less than this percent. Use 0 to always show all labels.
22
- # Defaults to 0
23
- attr_accessor :hide_labels_less_than
24
- # Affect the distance between the percentages and the pie chart
25
- # Defaults to 0.15
26
- attr_accessor :text_offset_percentage
27
- ## Use values instead of percentages
28
- attr_accessor :show_values_as_labels
24
+ # Defaults to +0+.
25
+ attr_writer :hide_labels_less_than
26
+
27
+ # Affect the distance between the percentages and the pie chart.
28
+ # Defaults to +0.15+.
29
+ attr_writer :text_offset_percentage
30
+
31
+ ## Use values instead of percentages.
32
+ attr_writer :show_values_as_labels
33
+
34
+ def initialize_store
35
+ @store = Gruff::Store.new(Gruff::Store::CustomData)
36
+ end
37
+ private :initialize_store
29
38
 
30
39
  def initialize_ivars
31
40
  super
@@ -34,90 +43,172 @@ class Gruff::Pie < Gruff::Base
34
43
  @text_offset_percentage = DEFAULT_TEXT_OFFSET_PERCENTAGE
35
44
  @show_values_as_labels = false
36
45
  end
46
+ private :initialize_ivars
47
+
48
+ def options
49
+ {
50
+ zero_degree: @zero_degree,
51
+ hide_labels_less_than: @hide_labels_less_than,
52
+ text_offset_percentage: @text_offset_percentage,
53
+ show_values_as_labels: @show_values_as_labels
54
+ }
55
+ end
37
56
 
38
57
  def draw
39
- @hide_line_markers = true
40
-
58
+ hide_line_markers
59
+
41
60
  super
42
61
 
43
- return unless @has_data
44
-
45
- diameter = @graph_height
46
- radius = ([@graph_width, @graph_height].min / 2.0) * 0.8
47
- center_x = @graph_left + (@graph_width / 2.0)
48
- center_y = @graph_top + (@graph_height / 2.0) - 10 # Move graph up a bit
49
- total_sum = sums_for_pie()
50
- prev_degrees = @zero_degree
51
-
52
- # Use full data since we can easily calculate percentages
53
- data = (@sort ? @data.sort{ |a, b| a[DATA_VALUES_INDEX].first <=> b[DATA_VALUES_INDEX].first } : @data)
54
- data.each do |data_row|
55
- if data_row[DATA_VALUES_INDEX].first > 0
56
- @d = @d.stroke data_row[DATA_COLOR_INDEX]
57
- @d = @d.fill 'transparent'
58
- @d.stroke_width(radius) # stroke width should be equal to radius. we'll draw centered on (radius / 2)
59
-
60
- current_degrees = (data_row[DATA_VALUES_INDEX].first / total_sum) * 360.0
61
-
62
- # ellipse will draw the the stroke centered on the first two parameters offset by the second two.
63
- # therefore, in order to draw a circle of the proper diameter we must center the stroke at
64
- # half the radius for both x and y
65
- @d = @d.ellipse(center_x, center_y,
66
- radius / 2.0, radius / 2.0,
67
- prev_degrees, prev_degrees + current_degrees + 0.5) # <= +0.5 'fudge factor' gets rid of the ugly gaps
68
-
69
- half_angle = prev_degrees + ((prev_degrees + current_degrees) - prev_degrees) / 2
70
-
71
- label_val = ((data_row[DATA_VALUES_INDEX].first / total_sum) * 100.0).round
72
- unless label_val < @hide_labels_less_than
73
- # RMagick must use sprintf with the string and % has special significance.
74
- label_string = @show_values_as_labels ? data_row[DATA_VALUES_INDEX].first.to_s : label_val.to_s + '%'
75
- @d = draw_label(center_x,center_y, half_angle,
76
- radius + (radius * @text_offset_percentage),
77
- label_string)
78
- end
79
-
80
- prev_degrees += current_degrees
62
+ return unless data_given?
63
+
64
+ slices.each do |slice|
65
+ if slice.value > 0
66
+ Gruff::Renderer::Ellipse.new(color: slice.color, width: radius)
67
+ .render(center_x, center_y, radius / 2.0, radius / 2.0, chart_degrees, chart_degrees + slice.degrees + 0.5)
68
+ process_label_for slice
69
+ update_chart_degrees_with slice.degrees
81
70
  end
82
71
  end
83
72
 
84
- # TODO debug a circle where the text is drawn...
85
-
86
- @d.draw(@base_image)
73
+ Gruff::Renderer.finish
87
74
  end
88
75
 
89
76
  private
90
77
 
91
- ##
92
- # Labels are drawn around a slightly wider ellipse to give room for
93
- # labels on the left and right.
94
- def draw_label(center_x, center_y, angle, radius, amount)
95
- # TODO Don't use so many hard-coded numbers
96
- r_offset = 20.0 # The distance out from the center of the pie to get point
97
- x_offset = center_x # + 15.0 # The label points need to be tweaked slightly
98
- y_offset = center_y # This one doesn't though
99
- radius_offset = (radius + r_offset)
100
- ellipse_factor = radius_offset * @text_offset_percentage
101
- x = x_offset + ((radius_offset + ellipse_factor) * Math.cos(deg2rad(angle)))
102
- y = y_offset + (radius_offset * Math.sin(deg2rad(angle)))
103
-
104
- # Draw label
105
- @d.fill = @font_color
106
- @d.font = @font if @font
107
- @d.pointsize = scale_fontsize(@marker_font_size)
108
- @d.stroke = 'transparent'
109
- @d.font_weight = BoldWeight
110
- @d.gravity = CenterGravity
111
- @d.annotate_scaled( @base_image,
112
- 0, 0,
113
- x, y,
114
- amount, @scale)
115
- end
116
-
117
- def sums_for_pie
118
- total_sum = 0.0
119
- @data.collect {|data_row| total_sum += data_row[DATA_VALUES_INDEX].first }
120
- total_sum
78
+ def slices
79
+ @slices ||= begin
80
+ slices = store.data.map { |data| slice_class.new(data, options) }
81
+
82
+ slices.sort_by(&:value) if @sort
83
+
84
+ total = slices.map(&:value).inject(:+).to_f
85
+ slices.each { |slice| slice.total = total }
86
+ end
87
+ end
88
+
89
+ # General Helper Methods
90
+
91
+ def hide_line_markers
92
+ @hide_line_markers = true
93
+ end
94
+
95
+ def update_chart_degrees_with(degrees)
96
+ @chart_degrees = chart_degrees + degrees
97
+ end
98
+
99
+ def slice_class
100
+ PieSlice
101
+ end
102
+
103
+ # Spatial Value-Related Methods
104
+
105
+ def chart_degrees
106
+ @chart_degrees ||= @zero_degree
107
+ end
108
+
109
+ attr_reader :graph_height
110
+
111
+ attr_reader :graph_width
112
+
113
+ def diameter
114
+ graph_height
115
+ end
116
+
117
+ def half_width
118
+ graph_width / 2.0
119
+ end
120
+
121
+ def half_height
122
+ graph_height / 2.0
123
+ end
124
+
125
+ def radius
126
+ @radius ||= ([graph_width, graph_height].min / 2.0) * 0.8
127
+ end
128
+
129
+ def center_x
130
+ @center_x ||= @graph_left + half_width
131
+ end
132
+
133
+ def center_y
134
+ @center_y ||= @graph_top + half_height - 10
135
+ end
136
+
137
+ def distance_from_center
138
+ 20.0
139
+ end
140
+
141
+ def radius_offset
142
+ radius + (radius * @text_offset_percentage) + distance_from_center
143
+ end
144
+
145
+ def ellipse_factor
146
+ radius_offset * @text_offset_percentage
147
+ end
148
+
149
+ # Label-Related Methods
150
+
151
+ def process_label_for(slice)
152
+ if slice.percentage >= @hide_labels_less_than
153
+ x, y = label_coordinates_for slice
154
+
155
+ draw_label(x, y, slice.label)
156
+ end
157
+ end
158
+
159
+ def label_coordinates_for(slice)
160
+ angle = chart_degrees + slice.degrees / 2
161
+
162
+ [x_label_coordinate(angle), y_label_coordinate(angle)]
121
163
  end
122
164
 
165
+ def x_label_coordinate(angle)
166
+ center_x + ((radius_offset + ellipse_factor) * Math.cos(deg2rad(angle)))
167
+ end
168
+
169
+ def y_label_coordinate(angle)
170
+ center_y + (radius_offset * Math.sin(deg2rad(angle)))
171
+ end
172
+
173
+ # Drawing-Related Methods
174
+
175
+ def draw_label(x, y, value)
176
+ text_renderer = Gruff::Renderer::Text.new(value, font: @font, size: @marker_font_size, color: @font_color, weight: Magick::BoldWeight)
177
+ text_renderer.add_to_render_queue(0, 0, x, y, Magick::CenterGravity)
178
+ end
179
+
180
+ # Helper Classes
181
+ #
182
+ # @private
183
+ class PieSlice < Struct.new(:data_array, :options)
184
+ attr_accessor :total
185
+
186
+ def name
187
+ data_array[0]
188
+ end
189
+
190
+ def value
191
+ data_array[1].first
192
+ end
193
+
194
+ def color
195
+ data_array[2]
196
+ end
197
+
198
+ def size
199
+ @size ||= value / total
200
+ end
201
+
202
+ def percentage
203
+ @percentage ||= (size * 100.0).round
204
+ end
205
+
206
+ def degrees
207
+ @degrees ||= size * 360.0
208
+ end
209
+
210
+ def label
211
+ options[:show_values_as_labels] ? value.to_s : "#{percentage}%"
212
+ end
213
+ end
123
214
  end