gruff 0.5.1-java → 0.10.0-java

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.
Files changed (120) 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 +130 -0
  7. data/.travis.yml +24 -12
  8. data/.yardopts +1 -0
  9. data/{History.txt → CHANGELOG.md} +61 -25
  10. data/Gemfile +3 -7
  11. data/README.md +57 -25
  12. data/Rakefile +6 -201
  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 +19 -14
  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 +337 -613
  26. data/lib/gruff/bezier.rb +34 -19
  27. data/lib/gruff/bullet.rb +51 -62
  28. data/lib/gruff/dot.rb +38 -62
  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 +59 -0
  33. data/lib/gruff/line.rb +130 -150
  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 +60 -84
  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 +27 -30
  42. data/lib/gruff/pie.rb +190 -93
  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 +34 -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 +127 -0
  53. data/lib/gruff/renderer/text.rb +42 -0
  54. data/lib/gruff/scatter.rb +156 -180
  55. data/lib/gruff/scene.rb +31 -41
  56. data/lib/gruff/side_bar.rb +77 -63
  57. data/lib/gruff/side_stacked_bar.rb +77 -60
  58. data/lib/gruff/spider.rb +37 -50
  59. data/lib/gruff/stacked_area.rb +32 -30
  60. data/lib/gruff/stacked_bar.rb +87 -49
  61. data/lib/gruff/store/base_data.rb +34 -0
  62. data/lib/gruff/store/custom_data.rb +34 -0
  63. data/lib/gruff/store/store.rb +80 -0
  64. data/lib/gruff/store/xy_data.rb +55 -0
  65. data/lib/gruff/themes.rb +32 -33
  66. data/lib/gruff/version.rb +3 -1
  67. metadata +88 -94
  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_legend.rb +0 -68
  107. data/test/test_line.rb +0 -657
  108. data/test/test_mini_bar.rb +0 -33
  109. data/test/test_mini_pie.rb +0 -25
  110. data/test/test_mini_side_bar.rb +0 -36
  111. data/test/test_net.rb +0 -231
  112. data/test/test_photo.rb +0 -41
  113. data/test/test_pie.rb +0 -154
  114. data/test/test_scatter.rb +0 -233
  115. data/test/test_scene.rb +0 -100
  116. data/test/test_side_bar.rb +0 -56
  117. data/test/test_sidestacked_bar.rb +0 -105
  118. data/test/test_spider.rb +0 -226
  119. data/test/test_stacked_area.rb +0 -52
  120. data/test/test_stacked_bar.rb +0 -52
data/init.rb CHANGED
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # For Rails
2
4
  require 'gruff'
@@ -1,8 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rmagick'
1
4
  require 'gruff/version'
2
5
 
3
6
  # Extra full path added to fix loading errors on some installations.
4
7
 
5
- %w(
8
+ %w[
9
+ patch/rmagick
10
+ patch/string
11
+
6
12
  themes
7
13
  base
8
14
  area
@@ -10,6 +16,7 @@ require 'gruff/version'
10
16
  bezier
11
17
  bullet
12
18
  dot
19
+ histogram
13
20
  line
14
21
  net
15
22
  pie
@@ -23,10 +30,27 @@ require 'gruff/version'
23
30
 
24
31
  scene
25
32
 
33
+ renderer/renderer
34
+ renderer/rectangle
35
+ renderer/circle
36
+ renderer/dash_line
37
+ renderer/line
38
+ renderer/polyline
39
+ renderer/polygon
40
+ renderer/bezier
41
+ renderer/ellipse
42
+ renderer/dot
43
+ renderer/text
44
+
45
+ store/store
46
+ store/base_data
47
+ store/custom_data
48
+ store/xy_data
49
+
26
50
  mini/legend
27
51
  mini/bar
28
52
  mini/pie
29
53
  mini/side_bar
30
- ).each do |filename|
54
+ ].each do |filename|
31
55
  require "gruff/#{filename}"
32
56
  end
@@ -1,18 +1,28 @@
1
- require File.dirname(__FILE__) + '/base'
1
+ # frozen_string_literal: true
2
2
 
3
- ##
4
- # A special bar graph that shows a single dataset as a set of
5
- # stacked bars. The bottom bar shows the running total and
6
- # the top bar shows the new value being added to the array.
3
+ require 'gruff/base'
7
4
 
5
+ #
6
+ # Gruff::AccumulatorBar is a special bar graph that shows a
7
+ # single dataset as a set of stacked bars.
8
+ # The bottom bar shows the running total and the top bar shows
9
+ # the new value being added to the array.
10
+ #
11
+ # Here's how to set up a Gruff::AccumulatorBar.
12
+ #
13
+ # g = Gruff::AccumulatorBar.new
14
+ # g.title = 'Your Savings'
15
+ # g.data 'First', [1, 1, 1]
16
+ # g.write('accumulator_bar.png')
17
+ #
8
18
  class Gruff::AccumulatorBar < Gruff::StackedBar
9
19
  def draw
10
- raise(Gruff::IncorrectNumberOfDatasetsException) unless @data.length == 1
20
+ raise(Gruff::IncorrectNumberOfDatasetsException) unless store.length == 1
11
21
 
12
- accum_array = @data.first[DATA_VALUES_INDEX][0..-2].inject([0]) { |a, v| a << a.last + v}
22
+ accum_array = store.data.first.points[0..-2].reduce([0]) { |a, v| a << a.last + v }
13
23
  data 'Accumulator', accum_array
14
24
  set_colors
15
- @data.reverse!
25
+ store.reverse!
16
26
  super
17
27
  end
18
28
  end
@@ -1,37 +1,54 @@
1
+ # frozen_string_literal: true
1
2
 
2
- require File.dirname(__FILE__) + '/base'
3
+ require 'gruff/base'
3
4
 
5
+ #
6
+ # Gruff::Area provides an area graph which displays graphically
7
+ # quantitative data.
8
+ #
9
+ # Here's how to set up a Gruff::Area.
10
+ #
11
+ # g = Gruff::Area.new
12
+ # g.title = 'Area Graph'
13
+ # g.data :Jimmy, [25, 36, 86, 39, 25, 31, 79, 88]
14
+ # g.data :Charles, [80, 54, 67, 54, 68, 70, 90, 95]
15
+ # g.data :Julie, [22, 29, 35, 38, 36, 40, 46, 57]
16
+ # g.write('area.png')
17
+ #
4
18
  class Gruff::Area < Gruff::Base
5
- def initialize(*)
19
+ # Specifies the filling opacity in area graph. Default is +0.85+.
20
+ attr_accessor :fill_opacity
21
+
22
+ # Specifies the stroke width in line around area graph. Default is +2.0+.
23
+ attr_accessor :stroke_width
24
+
25
+ def initialize_ivars
6
26
  super
7
27
  @sorted_drawing = true
28
+ @fill_opacity = 0.85
29
+ @stroke_width = 2.0
8
30
  end
31
+ private :initialize_ivars
9
32
 
10
33
  def draw
11
34
  super
12
35
 
13
- return unless @has_data
36
+ return unless data_given?
14
37
 
15
- @x_increment = @graph_width / (@column_count - 1).to_f
16
- @d = @d.stroke 'transparent'
38
+ x_increment = @graph_width / (column_count - 1).to_f
17
39
 
18
- @norm_data.each do |data_row|
19
- poly_points = Array.new
20
- prev_x = prev_y = 0.0
21
- @d = @d.fill data_row[DATA_COLOR_INDEX]
40
+ store.norm_data.each do |data_row|
41
+ poly_points = []
22
42
 
23
- data_row[DATA_VALUES_INDEX].each_with_index do |data_point, index|
43
+ data_row.points.each_with_index do |data_point, index|
24
44
  # Use incremented x and scaled y
25
- new_x = @graph_left + (@x_increment * index)
45
+ new_x = @graph_left + (x_increment * index)
26
46
  new_y = @graph_top + (@graph_height - data_point * @graph_height)
27
47
 
28
48
  poly_points << new_x
29
49
  poly_points << new_y
30
50
 
31
51
  draw_label(new_x, index)
32
-
33
- prev_x = new_x
34
- prev_y = new_y
35
52
  end
36
53
 
37
54
  # Add closing points, draw polygon
@@ -40,12 +57,9 @@ class Gruff::Area < Gruff::Base
40
57
  poly_points << @graph_left
41
58
  poly_points << @graph_bottom - 1
42
59
 
43
- @d = @d.polyline(*poly_points)
44
-
60
+ Gruff::Renderer::Polygon.new(color: data_row.color, width: @stroke_width, opacity: @fill_opacity).render(poly_points)
45
61
  end
46
62
 
47
- @d.draw(@base_image)
63
+ Gruff::Renderer.finish
48
64
  end
49
-
50
-
51
65
  end
@@ -1,24 +1,54 @@
1
- require File.dirname(__FILE__) + '/base'
2
- require File.dirname(__FILE__) + '/bar_conversion'
3
-
1
+ # frozen_string_literal: true
2
+
3
+ require 'gruff/base'
4
+ require 'gruff/helper/bar_conversion'
5
+
6
+ #
7
+ # Gruff::Bar provide a bar graph that presents categorical data
8
+ # with rectangular bars.
9
+ #
10
+ # Here's how to set up a Gruff::Bar.
11
+ #
12
+ # g = Gruff::Bar.new
13
+ # g.title = 'Bar Graph With Manual Colors'
14
+ # g.spacing_factor = 0.1
15
+ # g.group_spacing = 20
16
+ # g.data :Art, [0, 5, 8, 15], '#990000'
17
+ # g.data :Philosophy, [10, 3, 2, 8], '#009900'
18
+ # g.data :Science, [2, 15, 8, 11], '#990099'
19
+ # g.write('bar.png')
20
+ #
4
21
  class Gruff::Bar < Gruff::Base
5
-
6
- # Spacing factor applied between bars
22
+ # Spacing factor applied between bars.
7
23
  attr_accessor :bar_spacing
8
24
 
9
- def initialize(*args)
25
+ # Spacing factor applied between a group of bars belonging to the same label.
26
+ attr_accessor :group_spacing
27
+
28
+ # Set the number output format for labels using sprintf.
29
+ # Default is +"%.2f"+.
30
+ attr_accessor :label_formatting
31
+
32
+ # Output the values for the bars on a bar graph.
33
+ # Default is +false+.
34
+ attr_accessor :show_labels_for_bar_values
35
+
36
+ def initialize_ivars
10
37
  super
11
38
  @spacing_factor = 0.9
39
+ @label_formatting = nil
40
+ @show_labels_for_bar_values = false
12
41
  end
42
+ private :initialize_ivars
13
43
 
14
44
  def draw
15
45
  # Labels will be centered over the left of the bar if
16
- # there are more labels than columns. This is basically the same
46
+ # there are more labels than columns. This is basically the same
17
47
  # as where it would be for a line graph.
18
- @center_labels_over_point = (@labels.keys.length > @column_count ? true : false)
19
-
48
+ @center_labels_over_point = (@labels.keys.length > column_count)
49
+
20
50
  super
21
- return unless @has_data
51
+ return unless data_given?
22
52
 
23
53
  draw_bars
24
54
  end
@@ -28,9 +58,10 @@ class Gruff::Bar < Gruff::Base
28
58
  # and 1 means that each bars' width is nearly 0 (so each bar is a simple
29
59
  # line with no x dimension).
30
60
  #
31
- # Default value is 0.9.
61
+ # Default value is +0.9+.
32
62
  def spacing_factor=(space_percent)
33
- raise ArgumentError, 'spacing_factor must be between 0.00 and 1.00' unless (space_percent >= 0 and space_percent <= 1)
63
+ raise ArgumentError, 'spacing_factor must be between 0.00 and 1.00' unless (space_percent >= 0) && (space_percent <= 1)
64
+
34
65
  @spacing_factor = (1 - space_percent)
35
66
  end
36
67
 
@@ -41,68 +72,68 @@ protected
41
72
  #
42
73
  # Columns sit side-by-side.
43
74
  @bar_spacing ||= @spacing_factor # space between the bars
44
- @bar_width = @graph_width / (@column_count * @data.length).to_f
45
- padding = (@bar_width * (1 - @bar_spacing)) / 2
75
+ @group_spacing ||= 10
46
76
 
47
- @d = @d.stroke_opacity 0.0
77
+ bar_width = (@graph_width - calculate_spacing) / (column_count * store.length).to_f
78
+ padding = (bar_width * (1 - @bar_spacing)) / 2
48
79
 
49
80
  # Setup the BarConversion Object
50
- conversion = Gruff::BarConversion.new()
81
+ conversion = Gruff::BarConversion.new
51
82
  conversion.graph_height = @graph_height
52
83
  conversion.graph_top = @graph_top
53
84
 
54
85
  # Set up the right mode [1,2,3] see BarConversion for further explanation
55
- if @minimum_value >= 0 then
56
- # all bars go from zero to positiv
86
+ if minimum_value >= 0
87
+ # all bars go from zero to positive
57
88
  conversion.mode = 1
89
+ elsif maximum_value <= 0
90
+ # all bars go from 0 to negative
91
+ conversion.mode = 2
58
92
  else
59
- # all bars go from 0 to negativ
60
- if @maximum_value <= 0 then
61
- conversion.mode = 2
62
- else
63
- # bars either go from zero to negativ or to positiv
64
- conversion.mode = 3
65
- conversion.spread = @spread
66
- conversion.minimum_value = @minimum_value
67
- conversion.zero = -@minimum_value/@spread
68
- end
93
+ # bars either go from zero to negative or to positive
94
+ conversion.mode = 3
95
+ conversion.spread = @spread
96
+ conversion.minimum_value = minimum_value
97
+ conversion.zero = -minimum_value / @spread
69
98
  end
70
99
 
71
100
  # iterate over all normalised data
72
- @norm_data.each_with_index do |data_row, row_index|
101
+ store.norm_data.each_with_index do |data_row, row_index|
102
+ data_row.points.each_with_index do |data_point, point_index|
103
+ group_spacing = @group_spacing * @scale * point_index
73
104
 
74
- data_row[DATA_VALUES_INDEX].each_with_index do |data_point, point_index|
75
105
  # Use incremented x and scaled y
76
106
  # x
77
- left_x = @graph_left + (@bar_width * (row_index + point_index + ((@data.length - 1) * point_index))) + padding
78
- right_x = left_x + @bar_width * @bar_spacing
107
+ left_x = @graph_left + (bar_width * (row_index + point_index + ((store.length - 1) * point_index))) + padding + group_spacing
108
+ right_x = left_x + bar_width * @bar_spacing
79
109
  # y
80
- conv = []
81
- conversion.get_left_y_right_y_scaled( data_point, conv )
110
+ left_y, right_y = conversion.get_left_y_right_y_scaled(data_point)
82
111
 
83
112
  # create new bar
84
- @d = @d.fill data_row[DATA_COLOR_INDEX]
85
- @d = @d.rectangle(left_x, conv[0], right_x, conv[1])
113
+ rect_renderer = Gruff::Renderer::Rectangle.new(color: data_row.color)
114
+ rect_renderer.render(left_x, left_y, right_x, right_y)
86
115
 
87
116
  # Calculate center based on bar_width and current row
88
- label_center = @graph_left +
89
- (@data.length * @bar_width * point_index) +
90
- (@data.length * @bar_width / 2.0)
117
+ label_center = @graph_left + group_spacing + (store.length * bar_width * point_index) + (store.length * bar_width / 2.0)
91
118
 
92
119
  # Subtract half a bar width to center left if requested
93
- draw_label(label_center - (@center_labels_over_point ? @bar_width / 2.0 : 0.0), point_index)
120
+ draw_label(label_center, point_index)
94
121
  if @show_labels_for_bar_values
95
- val = (@label_formatting || '%.2f') % @norm_data[row_index][3][point_index]
96
- draw_value_label(left_x + (right_x - left_x)/2, conv[0]-30, val.commify, true)
122
+ raw_value = store.data[row_index].points[point_index]
123
+ val = (@label_formatting || '%.2f') % raw_value
124
+ y = raw_value >= 0 ? left_y - 30 : left_y + 12
125
+ draw_value_label(left_x + (right_x - left_x) / 2, y, val.commify, true)
97
126
  end
98
127
  end
99
-
100
128
  end
101
129
 
102
130
  # Draw the last label if requested
103
- draw_label(@graph_right, @column_count) if @center_labels_over_point
131
+ draw_label(@graph_right, column_count, Magick::NorthWestGravity) if @center_labels_over_point
104
132
 
105
- @d.draw(@base_image)
133
+ Gruff::Renderer.finish
106
134
  end
107
135
 
136
+ def calculate_spacing
137
+ @scale * @group_spacing * (column_count - 1)
138
+ end
108
139
  end
@@ -1,8 +1,7 @@
1
- require 'rubygems'
2
- require 'RMagick'
3
- require 'bigdecimal'
1
+ # frozen_string_literal: true
4
2
 
5
- require File.dirname(__FILE__) + '/deprecated'
3
+ require 'rmagick'
4
+ require 'bigdecimal'
6
5
 
7
6
  ##
8
7
  # = Gruff. Graphs.
@@ -16,48 +15,32 @@ require File.dirname(__FILE__) + '/deprecated'
16
15
  # David Stokar, Paul Rogers, Dave Woodward, Frank Oxener, Kevin Clark, Cies
17
16
  # Breijs, Richard Cowin, and a cast of thousands.
18
17
  #
19
- # See Gruff::Base#theme= for setting themes.
20
-
18
+ # See {Gruff::Base#theme=} for setting themes.
21
19
  module Gruff
22
20
  class Base
23
-
24
- include Magick
25
- include Deprecated
26
-
27
- # Draw extra lines showing where the margins and text centers are
28
- DEBUG = false
29
-
30
- # Used for navigating the array of data to plot
31
- DATA_LABEL_INDEX = 0
32
- DATA_VALUES_INDEX = 1
33
- DATA_COLOR_INDEX = 2
34
- DATA_VALUES_X_INDEX = 3
35
-
36
- # Space around text elements. Mostly used for vertical spacing
21
+ # Space around text elements. Mostly used for vertical spacing.
37
22
  LEGEND_MARGIN = TITLE_MARGIN = 20.0
38
23
  LABEL_MARGIN = 10.0
39
24
  DEFAULT_MARGIN = 20.0
40
25
 
41
- DEFAULT_TARGET_WIDTH = 800
26
+ DEFAULT_TARGET_WIDTH = 800.0
42
27
 
43
- THOUSAND_SEPARATOR = ','
44
-
45
- # Blank space above the graph
28
+ # Blank space above the graph. Default is +20+.
46
29
  attr_accessor :top_margin
47
30
 
48
- # Blank space below the graph
31
+ # Blank space below the graph. Default is +20+.
49
32
  attr_accessor :bottom_margin
50
33
 
51
- # Blank space to the right of the graph
34
+ # Blank space to the right of the graph. Default is +20+.
52
35
  attr_accessor :right_margin
53
36
 
54
- # Blank space to the left of the graph
37
+ # Blank space to the left of the graph. Default is +20+.
55
38
  attr_accessor :left_margin
56
39
 
57
- # Blank space below the title
40
+ # Blank space below the title. Default is +20+.
58
41
  attr_accessor :title_margin
59
42
 
60
- # Blank space below the legend
43
+ # Blank space below the legend. Default is +20+.
61
44
  attr_accessor :legend_margin
62
45
 
63
46
  # A hash of names for the individual columns, where the key is the array
@@ -65,7 +48,8 @@ module Gruff
65
48
  #
66
49
  # Not all columns need to be named.
67
50
  #
68
- # Example: 0 => 2005, 3 => 2006, 5 => 2007, 7 => 2008
51
+ # @example
52
+ # { 0 => 2005, 3 => 2006, 5 => 2007, 7 => 2008 }
69
53
  attr_accessor :labels
70
54
 
71
55
  # Used internally for spacing.
@@ -73,155 +57,134 @@ module Gruff
73
57
  # By default, labels are centered over the point they represent.
74
58
  attr_accessor :center_labels_over_point
75
59
 
76
- # Used internally for horizontal graph types.
60
+ # Used internally for horizontal graph types. Default is +false+.
77
61
  attr_accessor :has_left_labels
78
62
 
79
- # A label for the bottom of the graph
63
+ # Set a label for the bottom of the graph.
80
64
  attr_accessor :x_axis_label
81
65
 
82
- # A label for the left side of the graph
66
+ # Set a label for the left side of the graph.
83
67
  attr_accessor :y_axis_label
84
68
 
85
- # attr_accessor :x_axis_increment
69
+ # Set increment of the vertical marking lines.
70
+ attr_accessor :x_axis_increment
86
71
 
87
- # Manually set increment of the horizontal marking lines
72
+ # Set increment of the horizontal marking lines.
88
73
  attr_accessor :y_axis_increment
89
74
 
90
- # Height of staggering between labels (Bar graph only)
75
+ # Height of staggering between labels (Bar graph only).
91
76
  attr_accessor :label_stagger_height
92
77
 
93
- # Truncates labels if longer than max specified
78
+ # Truncates labels if longer than max specified.
94
79
  attr_accessor :label_max_size
95
80
 
96
- # How truncated labels visually appear if they exceed label_max_size
97
- # :absolute - does not show trailing dots to indicate truncation. This is
98
- # the default.
99
- # :trailing_dots - shows trailing dots to indicate truncation (note
100
- # that label_max_size must be greater than 3).
81
+ # How truncated labels visually appear if they exceed {#label_max_size}.
82
+ #
83
+ # - +:absolute+ - does not show trailing dots to indicate truncation. This is the default.
84
+ # - +:trailing_dots+ - shows trailing dots to indicate truncation (note that {#label_max_size}
85
+ # must be greater than 3).
101
86
  attr_accessor :label_truncation_style
102
87
 
103
88
  # Get or set the list of colors that will be used to draw the bars or lines.
104
89
  attr_accessor :colors
105
90
 
106
- # The large title of the graph displayed at the top
91
+ # Set the large title of the graph displayed at the top.
107
92
  attr_accessor :title
108
93
 
109
94
  # Font used for titles, labels, etc. Works best if you provide the full
110
95
  # path to the TTF font file. RMagick must be built with the Freetype
111
96
  # libraries for this to work properly.
112
- #
113
- # Tries to find Bitstream Vera (Vera.ttf) in the location specified by
114
- # ENV['MAGICK_FONT_PATH']. Uses default RMagick font otherwise.
115
- #
116
- # The font= method below fulfills the role of the writer, so we only need
117
- # a reader here.
118
97
  attr_reader :font
119
98
 
99
+ # Same as {#font} but for the title.
100
+ attr_accessor :title_font
101
+
102
+ # Specifies whether to draw the title bolded or not. Default is +true+.
103
+ attr_accessor :bold_title
104
+
105
+ # Specifies the text color.
120
106
  attr_accessor :font_color
121
107
 
122
- # Prevent drawing of line markers
108
+ # Prevent drawing of line markers. Default is +false+.
123
109
  attr_accessor :hide_line_markers
124
110
 
125
- # Prevent drawing of the legend
111
+ # Prevent drawing of the legend. Default is +false+.
126
112
  attr_accessor :hide_legend
127
113
 
128
- # Prevent drawing of the title
114
+ # Prevent drawing of the title. Default is +false+.
129
115
  attr_accessor :hide_title
130
116
 
131
- # Prevent drawing of line numbers
117
+ # Prevent drawing of line numbers. Default is +false+.
132
118
  attr_accessor :hide_line_numbers
133
119
 
134
- # Message shown when there is no data. Fits up to 20 characters. Defaults
135
- # to "No Data."
120
+ # Set a message shown when there is no data. Fits up to 20 characters. Defaults
121
+ # to +"No Data."+.
136
122
  attr_accessor :no_data_message
137
123
 
138
- # The font size of the large title at the top of the graph
124
+ # Set the font size of the large title at the top of the graph. Default is +36+.
139
125
  attr_accessor :title_font_size
140
126
 
141
127
  # Optionally set the size of the font. Based on an 800x600px graph.
142
- # Default is 20.
128
+ # Default is +20+.
143
129
  #
144
130
  # Will be scaled down if the graph is smaller than 800px wide.
145
131
  attr_accessor :legend_font_size
146
132
 
147
- # Display the legend under the graph
133
+ # Display the legend under the graph. Default is +false+.
148
134
  attr_accessor :legend_at_bottom
149
135
 
150
- # The font size of the labels around the graph
136
+ # The font size of the labels around the graph. Default is +21+.
151
137
  attr_accessor :marker_font_size
152
138
 
153
- # The color of the auxiliary lines
139
+ # Set the color of the auxiliary lines.
154
140
  attr_accessor :marker_color
141
+
142
+ # Set the shadow color of the auxiliary lines.
155
143
  attr_accessor :marker_shadow_color
156
144
 
157
- # The number of horizontal lines shown for reference
145
+ # Set the number of horizontal lines shown for reference.
158
146
  attr_accessor :marker_count
159
147
 
160
- # You can manually set a minimum value instead of having the values
161
- # guessed for you.
162
- #
163
- # Set it after you have given all your data to the graph object.
164
- attr_accessor :minimum_value
165
-
166
- # You can manually set a maximum value, such as a percentage-based graph
167
- # that always goes to 100.
168
- #
169
- # If you use this, you must set it after you have given all your data to
170
- # the graph object.
171
- attr_accessor :maximum_value
172
-
173
- # Set to true if you want the data sets sorted with largest avg values drawn
174
- # first.
148
+ # Set to +true+ if you want the data sets sorted with largest avg values drawn
149
+ # first. Default is +false+.
175
150
  attr_accessor :sort
176
151
 
177
- # Set to true if you want the data sets drawn with largest avg values drawn
178
- # first. This does not affect the legend.
152
+ # Set to +true+ if you want the data sets drawn with largest avg values drawn
153
+ # first. This does not affect the legend. Default is +false+.
179
154
  attr_accessor :sorted_drawing
180
155
 
181
- # Experimental
182
- attr_accessor :additional_line_values
183
-
184
- # Experimental
185
- attr_accessor :stacked
186
-
187
156
  # Optionally set the size of the colored box by each item in the legend.
188
- # Default is 20.0
157
+ # Default is +20.0+.
189
158
  #
190
159
  # Will be scaled down if graph is smaller than 800px wide.
191
160
  attr_accessor :legend_box_size
192
161
 
193
- # Output the values for the bars on a bar graph
194
- # Default is false
195
- attr_accessor :show_labels_for_bar_values
196
-
197
- # Set the number output format for labels using sprintf
198
- # Default is "%.2f"
199
- attr_accessor :label_formatting
200
-
201
- # With Side Bars use the data label for the marker value to the left of the bar
202
- # Default is false
162
+ # With Side Bars use the data label for the marker value to the left of the bar.
163
+ # Default is +false+.
203
164
  attr_accessor :use_data_label
165
+
204
166
  # If one numerical argument is given, the graph is drawn at 4/3 ratio
205
- # according to the given width (800 results in 800x600, 400 gives 400x300,
167
+ # according to the given width (+800+ results in 800x600, +400+ gives 400x300,
206
168
  # etc.).
207
169
  #
208
- # Or, send a geometry string for other ratios ('800x400', '400x225').
170
+ # Or, send a geometry string for other ratios ( +'800x400'+, +'400x225'+).
209
171
  #
210
- # Looks for Bitstream Vera as the default font. Expects an environment var
211
- # of MAGICK_FONT_PATH to be set. (Uses RMagick's default font otherwise.)
212
- def initialize(target_width=DEFAULT_TARGET_WIDTH)
213
- if Numeric === target_width
214
- @columns = target_width.to_f
215
- @rows = target_width.to_f * 0.75
216
- else
172
+ # @param target_width [Numeric, String] The graph image width.
173
+ #
174
+ def initialize(target_width = DEFAULT_TARGET_WIDTH)
175
+ if target_width.is_a?(String)
217
176
  geometric_width, geometric_height = target_width.split('x')
218
177
  @columns = geometric_width.to_f
219
178
  @rows = geometric_height.to_f
179
+ else
180
+ @columns = target_width.to_f
181
+ @rows = target_width.to_f * 0.75
220
182
  end
183
+ @columns.freeze
184
+ @rows.freeze
221
185
 
222
186
  initialize_ivars
223
187
 
224
- reset_themes
225
188
  self.theme = Themes::KEYNOTE
226
189
  end
227
190
 
@@ -233,22 +196,25 @@ module Gruff
233
196
  # developers to change this values in their program.
234
197
  def initialize_ivars
235
198
  # Internal for calculations
236
- @raw_columns = 800.0
237
- @raw_rows = 800.0 * (@rows/@columns)
238
- @column_count = 0
199
+ @raw_columns = DEFAULT_TARGET_WIDTH
200
+ @raw_rows = DEFAULT_TARGET_WIDTH * (@rows / @columns)
201
+ @raw_columns.freeze
202
+ @raw_rows.freeze
203
+
204
+ @scale = @columns / @raw_columns
205
+ @scale.freeze
206
+
239
207
  @marker_count = nil
240
208
  @maximum_value = @minimum_value = nil
241
- @has_data = false
242
- @data = Array.new
243
- @labels = Hash.new
244
- @labels_seen = Hash.new
209
+ @increment = nil
210
+ @labels = {}
245
211
  @sort = false
212
+ @sorted_drawing = false
246
213
  @title = nil
214
+ @title_font = nil
247
215
 
248
- @scale = @columns / @raw_columns
249
-
250
- vera_font_path = File.expand_path('Vera.ttf', ENV['MAGICK_FONT_PATH'])
251
- @font = File.exists?(vera_font_path) ? vera_font_path : nil
216
+ @font = nil
217
+ @bold_title = true
252
218
 
253
219
  @marker_font_size = 21.0
254
220
  @legend_font_size = 20.0
@@ -262,85 +228,95 @@ module Gruff
262
228
 
263
229
  @no_data_message = 'No Data'
264
230
 
265
- @hide_line_markers = @hide_legend = @hide_title = @hide_line_numbers = @legend_at_bottom = @show_labels_for_bar_values = false
231
+ @hide_line_markers = @hide_legend = @hide_title = @hide_line_numbers = @legend_at_bottom = false
266
232
  @center_labels_over_point = true
267
233
  @has_left_labels = false
268
234
  @label_stagger_height = 0
269
235
  @label_max_size = 0
270
236
  @label_truncation_style = :absolute
271
237
 
272
- @additional_line_values = []
273
- @additional_line_colors = []
274
238
  @theme_options = {}
275
239
 
240
+ @use_data_label = false
241
+ @x_axis_increment = nil
276
242
  @x_axis_label = @y_axis_label = nil
277
243
  @y_axis_increment = nil
278
- @stacked = nil
279
- @norm_data = nil
244
+
245
+ @store = Gruff::Store.new(Gruff::Store::BaseData)
280
246
  end
247
+ protected :initialize_ivars
281
248
 
282
249
  # Sets the top, bottom, left and right margins to +margin+.
250
+ #
251
+ # @param margin [Numeric] The margin size.
252
+ #
283
253
  def margins=(margin)
284
254
  @top_margin = @left_margin = @right_margin = @bottom_margin = margin
285
255
  end
286
256
 
287
257
  # Sets the font for graph text to the font at +font_path+.
258
+ #
259
+ # @param font_path [String] The path to font.
260
+ #
288
261
  def font=(font_path)
289
262
  @font = font_path
290
- @d.font = @font
263
+ Gruff::Renderer.font = @font
291
264
  end
292
265
 
293
266
  # Add a color to the list of available colors for lines.
294
267
  #
295
- # Example:
296
- # add_color('#c0e9d3')
268
+ # @param colorname [String] The color.
269
+ #
270
+ # @example
271
+ # add_color('#c0e9d3')
297
272
  def add_color(colorname)
298
273
  @colors << colorname
299
274
  end
300
275
 
301
276
  # Replace the entire color list with a new array of colors. Also
302
- # aliased as the colors= setter method.
277
+ # aliased as the {#colors=} setter method.
303
278
  #
304
279
  # If you specify fewer colors than the number of datasets you intend
305
- # to draw, 'increment_color' will cycle through the array, reusing
306
- # colors as needed.
280
+ # to draw, it will cycle through the array, reusing colors as needed.
307
281
  #
308
- # Note that (as with the 'theme' method), you should set up your color
309
- # list before you send your data (via the 'data' method). Calls to the
310
- # 'data' method made prior to this call will use whatever color scheme
282
+ # Note that (as with the {#theme=} method), you should set up your color
283
+ # list before you send your data (via the {#data} method). Calls to the
284
+ # {#data} method made prior to this call will use whatever color scheme
311
285
  # was in place at the time data was called.
312
286
  #
313
- # Example:
314
- # replace_colors ['#cc99cc', '#d9e043', '#34d8a2']
315
- def replace_colors(color_list=[])
287
+ # @param color_list [Array] The array of colors.
288
+ #
289
+ # @example
290
+ # replace_colors ['#cc99cc', '#d9e043', '#34d8a2']
291
+ def replace_colors(color_list = [])
316
292
  @colors = color_list
317
- @color_index = 0
318
293
  end
319
294
 
320
295
  # You can set a theme manually. Assign a hash to this method before you
321
296
  # send your data.
322
297
  #
323
- # graph.theme = {
324
- # :colors => %w(orange purple green white red),
325
- # :marker_color => 'blue',
326
- # :background_colors => ['black', 'grey', :top_bottom]
327
- # }
298
+ # graph.theme = {
299
+ # colors: %w(orange purple green white red),
300
+ # marker_color: 'blue',
301
+ # background_colors: ['black', 'grey', :top_bottom]
302
+ # }
328
303
  #
329
- # :background_image => 'squirrel.png' is also possible.
304
+ # +background_image: 'squirrel.png'+ is also possible.
330
305
  #
331
306
  # (Or hopefully something better looking than that.)
332
307
  #
308
+ # @param options [Hash] The optional setting for theme
309
+ #
333
310
  def theme=(options)
334
311
  reset_themes
335
312
 
336
313
  defaults = {
337
- :colors => %w(black white),
338
- :additional_line_colors => [],
339
- :marker_color => 'white',
340
- :marker_shadow_color => nil,
341
- :font_color => 'black',
342
- :background_colors => nil,
343
- :background_image => nil
314
+ colors: %w[black white],
315
+ marker_color: 'white',
316
+ marker_shadow_color: nil,
317
+ font_color: 'black',
318
+ background_colors: nil,
319
+ background_image: nil
344
320
  }
345
321
  @theme_options = defaults.merge options
346
322
 
@@ -348,35 +324,42 @@ module Gruff
348
324
  @marker_color = @theme_options[:marker_color]
349
325
  @marker_shadow_color = @theme_options[:marker_shadow_color]
350
326
  @font_color = @theme_options[:font_color] || @marker_color
351
- @additional_line_colors = @theme_options[:additional_line_colors]
352
327
 
353
- render_background
328
+ Gruff::Renderer.setup(@columns, @rows, @font, @scale, @theme_options)
354
329
  end
355
330
 
331
+ # Apply Apple's keynote theme.
356
332
  def theme_keynote
357
333
  self.theme = Themes::KEYNOTE
358
334
  end
359
335
 
336
+ # Apply 37signals theme.
360
337
  def theme_37signals
361
338
  self.theme = Themes::THIRTYSEVEN_SIGNALS
362
339
  end
363
340
 
341
+ # Apply Rails theme.
364
342
  def theme_rails_keynote
365
343
  self.theme = Themes::RAILS_KEYNOTE
366
344
  end
367
345
 
346
+ # Apply Odeo theme.
368
347
  def theme_odeo
369
348
  self.theme = Themes::ODEO
370
349
  end
371
350
 
351
+ # Apply pastel theme.
372
352
  def theme_pastel
373
353
  self.theme = Themes::PASTEL
374
354
  end
375
355
 
356
+ # Apply greyscale theme.
376
357
  def theme_greyscale
377
358
  self.theme = Themes::GREYSCALE
378
359
  end
379
360
 
361
+ # Input the data in the graph.
362
+ #
380
363
  # Parameters are an array where the first element is the name of the dataset
381
364
  # and the value is an array of values to plot.
382
365
  #
@@ -386,62 +369,63 @@ module Gruff
386
369
  # If the color argument is nil, the next color from the default theme will
387
370
  # be used.
388
371
  #
389
- # NOTE: If you want to use a preset theme, you must set it before calling
390
- # data().
372
+ # @param name [String, Symbol] The name of the dataset.
373
+ # @param data_points [Array] The array of dataset.
374
+ # @param color [String] The color for drawing graph of dataset.
391
375
  #
392
- # Example:
376
+ # @note
377
+ # If you want to use a preset theme, you must set it before calling {#data}.
378
+ #
379
+ # @example
393
380
  # data("Bart S.", [95, 45, 78, 89, 88, 76], '#ffcc00')
394
- def data(name, data_points=[], color=nil)
395
- data_points = Array(data_points) # make sure it's an array
396
- @data << [name, data_points, color]
397
- # Set column count if this is larger than previous counts
398
- @column_count = (data_points.length > @column_count) ? data_points.length : @column_count
399
-
400
- # Pre-normalize
401
- data_points.each do |data_point|
402
- next if data_point.nil?
403
-
404
- # Setup max/min so spread starts at the low end of the data points
405
- if @maximum_value.nil? && @minimum_value.nil?
406
- @maximum_value = @minimum_value = data_point
407
- end
381
+ def data(name, data_points = [], color = nil)
382
+ store.add(name, data_points, color)
383
+ end
408
384
 
409
- # TODO Doesn't work with stacked bar graphs
410
- # Original: @maximum_value = larger_than_max?(data_point, index) ? max(data_point, index) : @maximum_value
411
- @maximum_value = larger_than_max?(data_point) ? data_point : @maximum_value
412
- @has_data = true if @maximum_value >= 0
385
+ # You can manually set a minimum value instead of having the values
386
+ # guessed for you.
387
+ #
388
+ # Set it after you have given all your data to the graph object.
389
+ def minimum_value
390
+ @minimum_value || store.min
391
+ end
392
+ attr_writer :minimum_value
413
393
 
414
- @minimum_value = less_than_min?(data_point) ? data_point : @minimum_value
415
- @has_data = true if @minimum_value < 0
416
- end
394
+ # You can manually set a maximum value, such as a percentage-based graph
395
+ # that always goes to 100.
396
+ #
397
+ # If you use this, you must set it after you have given all your data to
398
+ # the graph object.
399
+ def maximum_value
400
+ @maximum_value || store.max
417
401
  end
402
+ attr_writer :maximum_value
418
403
 
419
- # Writes the graph to a file. Defaults to 'graph.png'
404
+ # Writes the graph to a file. Defaults to +'graph.png'+
405
+ #
406
+ # @param file_name [String] The file name of output image.
420
407
  #
421
- # Example:
408
+ # @example
422
409
  # write('graphs/my_pretty_graph.png')
423
- def write(filename='graph.png')
410
+ def write(file_name = 'graph.png')
424
411
  draw
425
- @base_image.write(filename)
412
+ Gruff::Renderer.write(file_name)
426
413
  end
427
414
 
428
415
  # Return the graph as a rendered binary blob.
429
- def to_blob(fileformat='PNG')
416
+ def to_blob(file_format = 'PNG')
430
417
  draw
431
- @base_image.to_blob do
432
- self.format = fileformat
433
- end
418
+ Gruff::Renderer.to_blob(file_format)
434
419
  end
435
420
 
436
-
437
- protected
421
+ protected
438
422
 
439
423
  # Overridden by subclasses to do the actual plotting of the graph.
440
424
  #
441
425
  # Subclasses should start by calling super() for this method.
442
426
  def draw
443
427
  # Maybe should be done in one of the following functions for more granularity.
444
- unless @has_data
428
+ unless data_given?
445
429
  draw_no_data
446
430
  return
447
431
  end
@@ -449,14 +433,6 @@ module Gruff
449
433
  setup_data
450
434
  setup_drawing
451
435
 
452
- debug {
453
- # Outer margin
454
- @d.rectangle(@left_margin, @top_margin,
455
- @raw_columns - @right_margin, @raw_rows - @bottom_margin)
456
- # Graph area box
457
- @d.rectangle(@graph_left, @graph_top, @graph_right, @graph_bottom)
458
- }
459
-
460
436
  draw_legend
461
437
  draw_line_markers
462
438
  draw_axis_labels
@@ -466,10 +442,9 @@ module Gruff
466
442
  # Perform data manipulation before calculating chart measurements
467
443
  def setup_data # :nodoc:
468
444
  if @y_axis_increment && !@hide_line_markers
469
- @maximum_value = [@y_axis_increment, @maximum_value, (@maximum_value.to_f / @y_axis_increment).round * @y_axis_increment].max
470
- @minimum_value = [@minimum_value, (@minimum_value.to_f / @y_axis_increment).round * @y_axis_increment].min
445
+ self.maximum_value = [@y_axis_increment, maximum_value, (maximum_value.to_f / @y_axis_increment).round * @y_axis_increment].max
446
+ self.minimum_value = [minimum_value, (minimum_value.to_f / @y_axis_increment).round * @y_axis_increment].min
471
447
  end
472
- make_stacked if @stacked
473
448
  end
474
449
 
475
450
  # Calculates size of drawable area and generates normalized data.
@@ -479,6 +454,7 @@ module Gruff
479
454
  # * title
480
455
  def setup_drawing
481
456
  calculate_spread
457
+ calculate_increment
482
458
  sort_data if @sort # Sort data with avg largest values set first (for display)
483
459
  set_colors
484
460
  normalize
@@ -486,78 +462,71 @@ module Gruff
486
462
  sort_norm_data if @sorted_drawing # Sort norm_data with avg largest values set first (for display)
487
463
  end
488
464
 
489
- # Make copy of data with values scaled between 0-100
490
- def normalize(force=false)
491
- if @norm_data.nil? || force
492
- @norm_data = []
493
- return unless @has_data
494
-
495
- @data.each do |data_row|
496
- norm_data_points = []
497
- data_row[DATA_VALUES_INDEX].each do |data_point|
498
- if data_point.nil?
499
- norm_data_points << nil
500
- else
501
- norm_data_points << ((data_point.to_f - @minimum_value.to_f) / @spread)
502
- end
503
- end
504
- if @show_labels_for_bar_values
505
- @norm_data << [data_row[DATA_LABEL_INDEX], norm_data_points, data_row[DATA_COLOR_INDEX], data_row[DATA_VALUES_INDEX]]
506
- else
507
- @norm_data << [data_row[DATA_LABEL_INDEX], norm_data_points, data_row[DATA_COLOR_INDEX]]
508
- end
465
+ attr_reader :store
466
+
467
+ def data_given?
468
+ @data_given ||= begin
469
+ if store.empty?
470
+ false
471
+ else
472
+ minimum_value <= store.min || maximum_value >= store.max
509
473
  end
510
474
  end
511
475
  end
512
476
 
477
+ def column_count
478
+ store.columns
479
+ end
480
+
481
+ # Make copy of data with values scaled between 0-100
482
+ def normalize
483
+ store.normalize(minimum: minimum_value, spread: @spread)
484
+ end
485
+
513
486
  def calculate_spread # :nodoc:
514
- @spread = @maximum_value.to_f - @minimum_value.to_f
487
+ @spread = maximum_value.to_f - minimum_value.to_f
515
488
  @spread = @spread > 0 ? @spread : 1
516
489
  end
517
490
 
491
+ def hide_title?
492
+ @hide_title || @title.nil? || @title.empty?
493
+ end
494
+
518
495
  ##
519
496
  # Calculates size of drawable area, general font dimensions, etc.
520
497
 
521
498
  def setup_graph_measurements
522
- @marker_caps_height = @hide_line_markers ? 0 :
523
- calculate_caps_height(@marker_font_size)
524
- @title_caps_height = (@hide_title || @title.nil?) ? 0 :
525
- calculate_caps_height(@title_font_size) * @title.lines.to_a.size
526
- @legend_caps_height = @hide_legend ? 0 :
527
- calculate_caps_height(@legend_font_size)
499
+ @marker_caps_height = @hide_line_markers ? 0 : calculate_caps_height(@marker_font_size)
500
+ @title_caps_height = hide_title? ? 0 : calculate_caps_height(@title_font_size) * @title.lines.to_a.size
501
+ @legend_caps_height = @hide_legend ? 0 : calculate_caps_height(@legend_font_size)
528
502
 
529
503
  if @hide_line_markers
530
- (@graph_left,
531
- @graph_right_margin,
532
- @graph_bottom_margin) = [@left_margin, @right_margin, @bottom_margin]
504
+ @graph_left = @left_margin
505
+ @graph_right_margin = @right_margin
506
+ @graph_bottom_margin = @bottom_margin
533
507
  else
534
508
  if @has_left_labels
535
509
  longest_left_label_width = calculate_width(@marker_font_size,
536
- labels.values.inject('') { |value, memo| (value.to_s.length > memo.to_s.length) ? value : memo }) * 1.25
510
+ labels.values.reduce('') { |value, memo| (value.to_s.length > memo.to_s.length) ? value : memo }) * 1.25
537
511
  else
538
512
  longest_left_label_width = calculate_width(@marker_font_size,
539
- label(@maximum_value.to_f, @increment))
513
+ label(maximum_value.to_f, @increment))
540
514
  end
541
515
 
542
516
  # Shift graph if left line numbers are hidden
543
- line_number_width = @hide_line_numbers && !@has_left_labels ?
544
- 0.0 :
545
- (longest_left_label_width + LABEL_MARGIN * 2)
517
+ line_number_width = @hide_line_numbers && !@has_left_labels ? 0.0 : (longest_left_label_width + LABEL_MARGIN * 2)
546
518
 
547
- @graph_left = @left_margin +
548
- line_number_width +
549
- (@y_axis_label.nil? ? 0.0 : @marker_caps_height + LABEL_MARGIN * 2)
519
+ @graph_left = @left_margin + line_number_width + (@y_axis_label.nil? ? 0.0 : @marker_caps_height + LABEL_MARGIN * 2)
550
520
 
551
521
  # Make space for half the width of the rightmost column label.
552
522
  # Might be greater than the number of columns if between-style bar markers are used.
553
- last_label = @labels.keys.sort.last.to_i
554
- extra_room_for_long_label = (last_label >= (@column_count-1) && @center_labels_over_point) ?
555
- calculate_width(@marker_font_size, @labels[last_label]) / 2.0 :
556
- 0
523
+ last_label = @labels.keys.max.to_i
524
+ extra_room_for_long_label = begin
525
+ (last_label >= (column_count - 1) && @center_labels_over_point) ? calculate_width(@marker_font_size, @labels[last_label]) / 2.0 : 0
526
+ end
557
527
  @graph_right_margin = @right_margin + extra_room_for_long_label
558
528
 
559
- @graph_bottom_margin = @bottom_margin +
560
- @marker_caps_height + LABEL_MARGIN
529
+ @graph_bottom_margin = @bottom_margin + @marker_caps_height + LABEL_MARGIN
561
530
  end
562
531
 
563
532
  @graph_right = @raw_columns - @graph_right_margin
@@ -565,12 +534,17 @@ module Gruff
565
534
 
566
535
  # When @hide title, leave a title_margin space for aesthetics.
567
536
  # Same with @hide_legend
568
- @graph_top = @legend_at_bottom ? @top_margin : (@top_margin +
569
- (@hide_title ? title_margin : @title_caps_height + title_margin) +
570
- (@hide_legend ? legend_margin : @legend_caps_height + legend_margin))
537
+ @graph_top = begin
538
+ if @legend_at_bottom
539
+ @top_margin
540
+ else
541
+ @top_margin +
542
+ (hide_title? ? title_margin : @title_caps_height + title_margin) +
543
+ (@hide_legend ? legend_margin : @legend_caps_height + legend_margin)
544
+ end
545
+ end
571
546
 
572
- x_axis_label_height = @x_axis_label.nil? ? 0.0 :
573
- @marker_caps_height + LABEL_MARGIN
547
+ x_axis_label_height = @x_axis_label.nil? ? 0.0 : @marker_caps_height + LABEL_MARGIN
574
548
  # FIXME: Consider chart types other than bar
575
549
  @graph_bottom = @raw_rows - @graph_bottom_margin - x_axis_label_height - @label_stagger_height
576
550
  @graph_height = @graph_bottom - @graph_top
@@ -578,34 +552,21 @@ module Gruff
578
552
 
579
553
  # Draw the optional labels for the x axis and y axis.
580
554
  def draw_axis_labels
581
- unless @x_axis_label.nil?
555
+ if @x_axis_label
582
556
  # X Axis
583
557
  # Centered vertically and horizontally by setting the
584
558
  # height to 1.0 and the width to the width of the graph.
585
559
  x_axis_label_y_coordinate = @graph_bottom + LABEL_MARGIN * 2 + @marker_caps_height
586
560
 
587
- # TODO Center between graph area
588
- @d.fill = @font_color
589
- @d.font = @font if @font
590
- @d.stroke('transparent')
591
- @d.pointsize = scale_fontsize(@marker_font_size)
592
- @d.gravity = NorthGravity
593
- @d = @d.annotate_scaled(@base_image,
594
- @raw_columns, 1.0,
595
- 0.0, x_axis_label_y_coordinate,
596
- @x_axis_label, @scale)
597
- debug { @d.line 0.0, x_axis_label_y_coordinate, @raw_columns, x_axis_label_y_coordinate }
561
+ # TODO: Center between graph area
562
+ text_renderer = Gruff::Renderer::Text.new(@x_axis_label, font: @font, size: @marker_font_size, color: @font_color)
563
+ text_renderer.render(@raw_columns, 1.0, 0.0, x_axis_label_y_coordinate)
598
564
  end
599
565
 
600
- unless @y_axis_label.nil?
566
+ if @y_axis_label
601
567
  # Y Axis, rotated vertically
602
- @d.rotation = -90.0
603
- @d.gravity = CenterGravity
604
- @d = @d.annotate_scaled(@base_image,
605
- 1.0, @raw_rows,
606
- @left_margin + @marker_caps_height / 2.0, 0.0,
607
- @y_axis_label, @scale)
608
- @d.rotation = 90.0
568
+ text_renderer = Gruff::Renderer::Text.new(@y_axis_label, font: @font, size: @marker_font_size, color: @font_color, rotation: -90)
569
+ text_renderer.render(1.0, @raw_rows, @left_margin + @marker_caps_height / 2.0, 0.0, Magick::CenterGravity)
609
570
  end
610
571
  end
611
572
 
@@ -613,128 +574,53 @@ module Gruff
613
574
  def draw_line_markers
614
575
  return if @hide_line_markers
615
576
 
616
- @d = @d.stroke_antialias false
617
-
618
- if @y_axis_increment.nil?
619
- # Try to use a number of horizontal lines that will come out even.
620
- #
621
- # TODO Do the same for larger numbers...100, 75, 50, 25
622
- if @marker_count.nil?
623
- (3..7).each do |lines|
624
- if @spread % lines == 0.0
625
- @marker_count = lines
626
- break
627
- end
628
- end
629
- @marker_count ||= 4
630
- end
631
- @increment = (@spread > 0 && @marker_count > 0) ? significant(@spread / @marker_count) : 1
632
- else
633
- # TODO Make this work for negative values
634
- @marker_count = (@spread / @y_axis_increment).to_i
635
- @increment = @y_axis_increment
636
- end
637
- @increment_scaled = @graph_height.to_f / (@spread / @increment)
577
+ increment_scaled = @graph_height.to_f / (@spread / @increment)
638
578
 
639
579
  # Draw horizontal line markers and annotate with numbers
640
580
  (0..@marker_count).each do |index|
641
- y = @graph_top + @graph_height - index.to_f * @increment_scaled
642
-
643
- @d = @d.fill(@marker_color)
581
+ y = @graph_top + @graph_height - index.to_f * increment_scaled
644
582
 
645
- # FIXME(uwe): Workaround for Issue #66
646
- # https://github.com/topfunky/gruff/issues/66
647
- # https://github.com/rmagick/rmagick/issues/82
648
- # Remove if the issue gets fixed.
649
- y += 0.001 unless defined?(JRUBY_VERSION)
650
- # EMXIF
651
-
652
- @d = @d.line(@graph_left, y, @graph_right, y)
583
+ Gruff::Renderer::Line.new(color: @marker_color).render(@graph_left, y, @graph_right, y)
653
584
  #If the user specified a marker shadow color, draw a shadow just below it
654
- unless @marker_shadow_color.nil?
655
- @d = @d.fill(@marker_shadow_color)
656
- @d = @d.line(@graph_left, y + 1, @graph_right, y + 1)
585
+ if @marker_shadow_color
586
+ Gruff::Renderer::Line.new(color: @marker_shadow_color).render(@graph_left, y + 1, @graph_right, y + 1)
657
587
  end
658
588
 
659
- marker_label = BigDecimal(index.to_s) * BigDecimal(@increment.to_s) +
660
- BigDecimal(@minimum_value.to_s)
661
-
662
589
  unless @hide_line_numbers
663
- @d.fill = @font_color
664
- @d.font = @font if @font
665
- @d.stroke('transparent')
666
- @d.pointsize = scale_fontsize(@marker_font_size)
667
- @d.gravity = EastGravity
668
-
669
- # Vertically center with 1.0 for the height
670
- @d = @d.annotate_scaled(@base_image,
671
- @graph_left - LABEL_MARGIN, 1.0,
672
- 0.0, y,
673
- label(marker_label, @increment), @scale)
590
+ marker_label = BigDecimal(index.to_s) * BigDecimal(@increment.to_s) + BigDecimal(minimum_value.to_s)
591
+ label = label(marker_label, @increment)
592
+ text_renderer = Gruff::Renderer::Text.new(label, font: @font, size: @marker_font_size, color: @font_color)
593
+ text_renderer.render(@graph_left - LABEL_MARGIN, 1.0, 0.0, y, Magick::EastGravity)
674
594
  end
675
595
  end
676
-
677
- # # Submitted by a contibutor...the utility escapes me
678
- # i = 0
679
- # @additional_line_values.each do |value|
680
- # @increment_scaled = @graph_height.to_f / (@maximum_value.to_f / value)
681
- #
682
- # y = @graph_top + @graph_height - @increment_scaled
683
- #
684
- # @d = @d.stroke(@additional_line_colors[i])
685
- # @d = @d.line(@graph_left, y, @graph_right, y)
686
- #
687
- #
688
- # @d.fill = @additional_line_colors[i]
689
- # @d.font = @font if @font
690
- # @d.stroke('transparent')
691
- # @d.pointsize = scale_fontsize(@marker_font_size)
692
- # @d.gravity = EastGravity
693
- # @d = @d.annotate_scaled( @base_image,
694
- # 100, 20,
695
- # -10, y - (@marker_font_size/2.0),
696
- # "", @scale)
697
- # i += 1
698
- # end
699
-
700
- @d = @d.stroke_antialias true
701
596
  end
702
597
 
703
- ##
704
598
  # Return the sum of values in an array.
705
599
  #
706
600
  # Duplicated to not conflict with active_support in Rails.
707
-
708
601
  def sum(arr)
709
- arr.inject(0) { |i, m| m + i }
602
+ arr.reduce(0) { |i, m| m + i }
710
603
  end
711
604
 
712
- ##
713
605
  # Return a calculation of center
714
-
715
606
  def center(size)
716
607
  (@raw_columns - size) / 2
717
608
  end
718
609
 
719
- ##
720
610
  # Draws a legend with the names of the datasets matched
721
611
  # to the colors used to draw them.
722
-
723
612
  def draw_legend
724
613
  return if @hide_legend
725
614
 
726
- @legend_labels = @data.collect { |item| item[DATA_LABEL_INDEX] }
615
+ legend_labels = store.data.map(&:label)
727
616
 
728
617
  legend_square_width = @legend_box_size # small square with color of this item
729
618
 
730
619
  # May fix legend drawing problem at small sizes
731
- @d.font = @font if @font
732
- @d.pointsize = @legend_font_size
733
-
734
620
  label_widths = [[]] # Used to calculate line wrap
735
- @legend_labels.each do |label|
736
- metrics = @d.get_type_metrics(@base_image, label.to_s)
737
- label_width = metrics.width + legend_square_width * 2.7
621
+ legend_labels.each do |label|
622
+ width = calculate_width(@legend_font_size, label)
623
+ label_width = width + legend_square_width * 2.7
738
624
  label_widths.last.push label_width
739
625
 
740
626
  if sum(label_widths.last) > (@raw_columns * 0.9)
@@ -743,45 +629,38 @@ module Gruff
743
629
  end
744
630
 
745
631
  current_x_offset = center(sum(label_widths.first))
746
- current_y_offset = @legend_at_bottom ? @graph_height + title_margin : (@hide_title ?
747
- @top_margin + title_margin :
748
- @top_margin + title_margin + @title_caps_height)
632
+ current_y_offset = begin
633
+ if @legend_at_bottom
634
+ @graph_height + title_margin
635
+ else
636
+ hide_title? ? @top_margin + title_margin : @top_margin + title_margin + @title_caps_height
637
+ end
638
+ end
749
639
 
750
- @legend_labels.each_with_index do |legend_label, index|
640
+ legend_labels.each_with_index do |legend_label, index|
641
+ next if legend_label.empty?
751
642
 
752
643
  # Draw label
753
- @d.fill = @font_color
754
- @d.font = @font if @font
755
- @d.pointsize = scale_fontsize(@legend_font_size)
756
- @d.stroke('transparent')
757
- @d.font_weight = NormalWeight
758
- @d.gravity = WestGravity
759
- @d = @d.annotate_scaled(@base_image,
760
- @raw_columns, 1.0,
761
- current_x_offset + (legend_square_width * 1.7), current_y_offset,
762
- legend_label.to_s, @scale)
644
+ text_renderer = Gruff::Renderer::Text.new(legend_label, font: @font, size: @legend_font_size, color: @font_color)
645
+ text_renderer.render(@raw_columns, 1.0, current_x_offset + (legend_square_width * 1.7), current_y_offset, Magick::WestGravity)
763
646
 
764
647
  # Now draw box with color of this dataset
765
- @d = @d.stroke('transparent')
766
- @d = @d.fill @data[index][DATA_COLOR_INDEX]
767
- @d = @d.rectangle(current_x_offset,
768
- current_y_offset - legend_square_width / 2.0,
769
- current_x_offset + legend_square_width,
770
- current_y_offset + legend_square_width / 2.0)
648
+ rect_renderer = Gruff::Renderer::Rectangle.new(color: store.data[index].color)
649
+ rect_renderer.render(current_x_offset,
650
+ current_y_offset - legend_square_width / 2.0,
651
+ current_x_offset + legend_square_width,
652
+ current_y_offset + legend_square_width / 2.0)
771
653
 
772
- @d.pointsize = @legend_font_size
773
- metrics = @d.get_type_metrics(@base_image, legend_label.to_s)
774
- current_string_offset = metrics.width + (legend_square_width * 2.7)
654
+ width = calculate_width(legend_font_size, legend_label)
655
+ current_string_offset = width + (legend_square_width * 2.7)
775
656
 
776
657
  # Handle wrapping
777
658
  label_widths.first.shift
778
659
  if label_widths.first.empty?
779
- debug { @d.line 0.0, current_y_offset, @raw_columns, current_y_offset }
780
-
781
660
  label_widths.shift
782
661
  current_x_offset = center(sum(label_widths.first)) unless label_widths.empty?
783
662
  line_height = [@legend_caps_height, legend_square_width].max + legend_margin
784
- if label_widths.length > 0
663
+ unless label_widths.empty?
785
664
  # Wrap to next line and shrink available graph dimensions
786
665
  current_y_offset += line_height
787
666
  @graph_top += line_height
@@ -791,32 +670,29 @@ module Gruff
791
670
  current_x_offset += current_string_offset
792
671
  end
793
672
  end
794
- @color_index = 0
795
673
  end
796
674
 
797
675
  # Draws a title on the graph.
798
676
  def draw_title
799
- return if (@hide_title || @title.nil?)
800
-
801
- @d.fill = @font_color
802
- @d.font = @font if @font
803
- @d.stroke('transparent')
804
- @d.pointsize = scale_fontsize(@title_font_size)
805
- @d.font_weight = BoldWeight
806
- @d.gravity = NorthGravity
807
- @d = @d.annotate_scaled(@base_image,
808
- @raw_columns, 1.0,
809
- 0, @top_margin,
810
- @title, @scale)
677
+ return if hide_title?
678
+
679
+ font = @title_font || @font
680
+ font_weight = @bold_title ? Magick::BoldWeight : Magick::NormalWeight
681
+ font_size = @title_font_size
682
+
683
+ metrics = Renderer::Text.metrics(@title, font_size, font_weight)
684
+ if metrics.width > @raw_columns
685
+ font_size = font_size * (@raw_columns / metrics.width) * 0.95
686
+ end
687
+ text_renderer = Gruff::Renderer::Text.new(@title, font: font, size: font_size, color: @font_color, weight: font_weight)
688
+ text_renderer.render(@raw_columns, 1.0, 0, @top_margin)
811
689
  end
812
690
 
813
691
  # Draws column labels below graph, centered over x_offset
814
692
  #--
815
693
  # TODO Allow WestGravity as an option
816
- def draw_label(x_offset, index)
817
- return if @hide_line_markers
818
-
819
- if !@labels[index].nil? && @labels_seen[index].nil?
694
+ def draw_label(x_offset, index, gravity = Magick::NorthGravity)
695
+ draw_unique_label(index) do
820
696
  y_offset = @graph_bottom + LABEL_MARGIN
821
697
 
822
698
  # TESTME
@@ -824,138 +700,42 @@ module Gruff
824
700
  # TODO: See if index.odd? is the best stragegy
825
701
  y_offset += @label_stagger_height if index.odd?
826
702
 
827
- label_text = @labels[index]
828
-
829
- # TESTME
830
- # FIXME: Consider chart types other than bar
831
- if label_text.size > @label_max_size
832
- if @label_truncation_style == :trailing_dots
833
- if @label_max_size > 3
834
- # 4 because '...' takes up 3 chars
835
- label_text = "#{label_text[0 .. (@label_max_size - 4)]}..."
836
- end
837
- else # @label_truncation_style is :absolute (default)
838
- label_text = label_text[0 .. (@label_max_size - 1)]
839
- end
840
-
841
- end
703
+ label_text = truncate_label_text(labels[index].to_s)
842
704
 
843
705
  if x_offset >= @graph_left && x_offset <= @graph_right
844
- @d.fill = @font_color
845
- @d.font = @font if @font
846
- @d.stroke('transparent')
847
- @d.font_weight = NormalWeight
848
- @d.pointsize = scale_fontsize(@marker_font_size)
849
- @d.gravity = NorthGravity
850
- @d = @d.annotate_scaled(@base_image,
851
- 1.0, 1.0,
852
- x_offset, y_offset,
853
- label_text, @scale)
706
+ text_renderer = Gruff::Renderer::Text.new(label_text, font: @font, size: @marker_font_size, color: @font_color)
707
+ text_renderer.render(1.0, 1.0, x_offset, y_offset, gravity)
854
708
  end
709
+ end
710
+ end
711
+
712
+ def draw_unique_label(index)
713
+ return if @hide_line_markers
714
+
715
+ @labels_seen ||= {}
716
+ if !@labels[index].nil? && @labels_seen[index].nil?
717
+ yield
855
718
  @labels_seen[index] = 1
856
- debug { @d.line 0.0, y_offset, @raw_columns, y_offset }
857
719
  end
858
720
  end
859
721
 
860
722
  # Draws the data value over the data point in bar graphs
861
- def draw_value_label(x_offset, y_offset, data_point, bar_value=false)
723
+ def draw_value_label(x_offset, y_offset, data_point, bar_value = false)
862
724
  return if @hide_line_markers && !bar_value
863
725
 
864
- #y_offset = @graph_bottom + LABEL_MARGIN
865
-
866
- @d.fill = @font_color
867
- @d.font = @font if @font
868
- @d.stroke('transparent')
869
- @d.font_weight = NormalWeight
870
- @d.pointsize = scale_fontsize(@marker_font_size)
871
- @d.gravity = NorthGravity
872
- @d = @d.annotate_scaled(@base_image,
873
- 1.0, 1.0,
874
- x_offset, y_offset,
875
- data_point.to_s, @scale)
876
-
877
- debug { @d.line 0.0, y_offset, @raw_columns, y_offset }
726
+ text_renderer = Gruff::Renderer::Text.new(data_point, font: @font, size: @marker_font_size, color: @font_color)
727
+ text_renderer.render(1.0, 1.0, x_offset, y_offset)
878
728
  end
879
729
 
880
730
  # Shows an error message because you have no data.
881
731
  def draw_no_data
882
- @d.fill = @font_color
883
- @d.font = @font if @font
884
- @d.stroke('transparent')
885
- @d.font_weight = NormalWeight
886
- @d.pointsize = scale_fontsize(80)
887
- @d.gravity = CenterGravity
888
- @d = @d.annotate_scaled(@base_image,
889
- @raw_columns, @raw_rows/2.0,
890
- 0, 10,
891
- @no_data_message, @scale)
892
- end
893
-
894
- # Finds the best background to render based on the provided theme options.
895
- #
896
- # Creates a @base_image to draw on.
897
- def render_background
898
- case @theme_options[:background_colors]
899
- when Array
900
- @base_image = render_gradiated_background(@theme_options[:background_colors][0], @theme_options[:background_colors][1], @theme_options[:background_direction])
901
- when String
902
- @base_image = render_solid_background(@theme_options[:background_colors])
903
- else
904
- @base_image = render_image_background(*@theme_options[:background_image])
905
- end
906
- end
907
-
908
- # Make a new image at the current size with a solid +color+.
909
- def render_solid_background(color)
910
- Image.new(@columns, @rows) {
911
- self.background_color = color
912
- }
913
- end
914
-
915
- # Use with a theme definition method to draw a gradiated background.
916
- def render_gradiated_background(top_color, bottom_color, direct = :top_bottom)
917
- case direct
918
- when :bottom_top
919
- gradient_fill = GradientFill.new(0, 0, 100, 0, bottom_color, top_color)
920
- when :left_right
921
- gradient_fill = GradientFill.new(0, 0, 0, 100, top_color, bottom_color)
922
- when :right_left
923
- gradient_fill = GradientFill.new(0, 0, 0, 100, bottom_color, top_color)
924
- when :topleft_bottomright
925
- gradient_fill = GradientFill.new(0, 100, 100, 0, top_color, bottom_color)
926
- when :topright_bottomleft
927
- gradient_fill = GradientFill.new(0, 0, 100, 100, bottom_color, top_color)
928
- else
929
- gradient_fill = GradientFill.new(0, 0, 100, 0, top_color, bottom_color)
930
- end
931
- Image.new(@columns, @rows, gradient_fill)
932
- end
933
-
934
- # Use with a theme to use an image (800x600 original) background.
935
- def render_image_background(image_path)
936
- image = Image.read(image_path)
937
- if @scale != 1.0
938
- image[0].resize!(@scale) # TODO Resize with new scale (crop if necessary for wide graph)
939
- end
940
- image[0]
941
- end
942
-
943
- # Use with a theme to make a transparent background
944
- def render_transparent_background
945
- Image.new(@columns, @rows) do
946
- self.background_color = 'transparent'
947
- end
732
+ text_renderer = Gruff::Renderer::Text.new(@no_data_message, font: @font, size: 80, color: @font_color)
733
+ text_renderer.render(@raw_columns, @raw_rows / 2.0, 0, 10, Magick::CenterGravity)
948
734
  end
949
735
 
950
736
  # Resets everything to defaults (except data).
951
737
  def reset_themes
952
- @color_index = 0
953
- @labels_seen = {}
954
738
  @theme_options = {}
955
-
956
- @d = Draw.new
957
- # Scale down from 800x600 used to calculate drawing.
958
- @d = @d.scale(@scale, @scale)
959
739
  end
960
740
 
961
741
  def scale(value) # :nodoc:
@@ -971,17 +751,9 @@ module Gruff
971
751
  (value > max_value) ? max_value : value
972
752
  end
973
753
 
974
- # Overridden by subclasses such as stacked bar.
975
- def larger_than_max?(data_point) # :nodoc:
976
- data_point > @maximum_value
977
- end
978
-
979
- def less_than_min?(data_point) # :nodoc:
980
- data_point < @minimum_value
981
- end
982
-
983
754
  def significant(i) # :nodoc:
984
755
  return 1.0 if i == 0 # Keep from going into infinite loop
756
+
985
757
  inc = BigDecimal(i.to_s)
986
758
  factor = BigDecimal('1.0')
987
759
  while inc < 10
@@ -997,6 +769,8 @@ module Gruff
997
769
  res = inc.floor * factor
998
770
  if res.to_i.to_f == res
999
771
  res.to_i
772
+ elsif res.to_f == res
773
+ res.to_f
1000
774
  else
1001
775
  res
1002
776
  end
@@ -1004,69 +778,32 @@ module Gruff
1004
778
 
1005
779
  # Sort with largest overall summed value at front of array.
1006
780
  def sort_data
1007
- @data = @data.sort_by { |a| -a[DATA_VALUES_INDEX].inject(0) { |sum, num| sum + num.to_f } }
781
+ store.sort_data!
1008
782
  end
1009
783
 
1010
- # Set the color for each data set unless it was gived in the data(...) call.
784
+ # Set the color for each data set unless it was given in the data(...) call.
1011
785
  def set_colors
1012
- @data.each { |a| a[DATA_COLOR_INDEX] ||= increment_color }
786
+ store.change_colors(@colors)
1013
787
  end
1014
788
 
1015
789
  # Sort with largest overall summed value at front of array so it shows up
1016
790
  # correctly in the drawn graph.
1017
791
  def sort_norm_data
1018
- @norm_data =
1019
- @norm_data.sort_by { |a| -a[DATA_VALUES_INDEX].inject(0) { |sum, num| sum + num.to_f } }
1020
- end
1021
-
1022
- # Used by StackedBar and child classes.
1023
- #
1024
- # May need to be moved to the StackedBar class.
1025
- def get_maximum_by_stack
1026
- # Get sum of each stack
1027
- max_hash = {}
1028
- @data.each do |data_set|
1029
- data_set[DATA_VALUES_INDEX].each_with_index do |data_point, i|
1030
- max_hash[i] = 0.0 unless max_hash[i]
1031
- max_hash[i] += data_point.to_f
1032
- end
1033
- end
1034
-
1035
- # @maximum_value = 0
1036
- max_hash.keys.each do |key|
1037
- @maximum_value = max_hash[key] if max_hash[key] > @maximum_value
1038
- end
1039
- @minimum_value = 0
792
+ store.sort_norm_data!
1040
793
  end
1041
794
 
1042
- def make_stacked # :nodoc:
1043
- stacked_values = Array.new(@column_count, 0)
1044
- @data.each do |value_set|
1045
- value_set[DATA_VALUES_INDEX].each_with_index do |value, index|
1046
- stacked_values[index] += value
1047
- end
1048
- value_set[DATA_VALUES_INDEX] = stacked_values.dup
1049
- end
1050
- end
795
+ private
1051
796
 
1052
- private
797
+ def truncate_label_text(text)
798
+ return text if text.size <= @label_max_size
1053
799
 
1054
- # Takes a block and draws it if DEBUG is true.
1055
- #
1056
- # Example:
1057
- # debug { @d.rectangle x1, y1, x2, y2 }
1058
- def debug
1059
- if DEBUG
1060
- @d = @d.fill 'transparent'
1061
- @d = @d.stroke 'turquoise'
1062
- @d = yield
800
+ if @label_truncation_style == :trailing_dots
801
+ # 4 because '...' takes up 3 chars
802
+ text = "#{text[0..(@label_max_size - 4)]}..." if @label_max_size > 3
803
+ else
804
+ text = text[0..(@label_max_size - 1)]
1063
805
  end
1064
- end
1065
-
1066
- # Returns the next color in your color list.
1067
- def increment_color
1068
- @color_index = (@color_index + 1) % @colors.length
1069
- @colors[@color_index - 1]
806
+ text
1070
807
  end
1071
808
 
1072
809
  # Return a formatted string representing a number value that should be
@@ -1086,7 +823,7 @@ module Gruff
1086
823
  else
1087
824
  value.to_s
1088
825
  end
1089
- elsif (@spread.to_f % (@marker_count.to_f==0 ? 1 : @marker_count.to_f) == 0) || !@y_axis_increment.nil?
826
+ elsif (@spread.to_f % (@marker_count.to_f == 0 ? 1 : @marker_count.to_f) == 0) || !@y_axis_increment.nil?
1090
827
  value.to_i.to_s
1091
828
  elsif @spread > 10.0
1092
829
  sprintf('%0i', value)
@@ -1097,7 +834,7 @@ module Gruff
1097
834
  end
1098
835
 
1099
836
  parts = label.split('.')
1100
- parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{THOUSAND_SEPARATOR}")
837
+ parts[0] = parts[0].commify
1101
838
  parts.join('.')
1102
839
  end
1103
840
 
@@ -1107,9 +844,8 @@ module Gruff
1107
844
  # Not scaled since it deals with dimensions that the regular scaling will
1108
845
  # handle.
1109
846
  def calculate_caps_height(font_size)
1110
- @d.pointsize = font_size
1111
- @d.font = @font if @font
1112
- @d.get_type_metrics(@base_image, 'X').height
847
+ metrics = Renderer::Text.metrics('X', font_size)
848
+ metrics.height
1113
849
  end
1114
850
 
1115
851
  # Returns the width of a string at this pointsize.
@@ -1117,53 +853,41 @@ module Gruff
1117
853
  # Not scaled since it deals with dimensions that the regular
1118
854
  # scaling will handle.
1119
855
  def calculate_width(font_size, text)
1120
- return 0 if text.nil?
1121
- @d.pointsize = font_size
1122
- @d.font = @font if @font
1123
- @d.get_type_metrics(@base_image, text.to_s).width
1124
- end
856
+ text = text.to_s
857
+ return 0 if text.empty?
1125
858
 
1126
- end # Gruff::Base
1127
-
1128
- class IncorrectNumberOfDatasetsException < StandardError;
1129
- end
1130
-
1131
- end # Gruff
1132
-
1133
- module Magick
1134
-
1135
- class Draw
1136
-
1137
- # Additional method to scale annotation text since Draw.scale doesn't.
1138
- def annotate_scaled(img, width, height, x, y, text, scale)
1139
- scaled_width = (width * scale) >= 1 ? (width * scale) : 1
1140
- scaled_height = (height * scale) >= 1 ? (height * scale) : 1
1141
-
1142
- self.annotate(img,
1143
- scaled_width, scaled_height,
1144
- x * scale, y * scale,
1145
- text.gsub('%', '%%'))
859
+ metrics = Renderer::Text.metrics(text, font_size)
860
+ metrics.width
1146
861
  end
1147
862
 
1148
- if defined? JRUBY_VERSION
1149
- # FIXME(uwe): We should NOT need to implement this method.
1150
- # Remove this method as soon as RMagick4J Issue #16 is fixed.
1151
- # https://github.com/Serabe/RMagick4J/issues/16
1152
- def fill=(fill)
1153
- fill = {:white => '#FFFFFF'}[fill.to_sym] || fill
1154
- @draw.fill = Magick4J.ColorDatabase.query_default(fill)
1155
- self
863
+ def calculate_increment
864
+ if @y_axis_increment.nil?
865
+ # Try to use a number of horizontal lines that will come out even.
866
+ #
867
+ # TODO Do the same for larger numbers...100, 75, 50, 25
868
+ if @marker_count.nil?
869
+ (3..7).each do |lines|
870
+ if @spread % lines == 0.0
871
+ @marker_count = lines
872
+ break
873
+ end
874
+ end
875
+ @marker_count ||= 4
876
+ end
877
+ @increment = (@spread > 0 && @marker_count > 0) ? significant(@spread / @marker_count) : 1
878
+ else
879
+ # TODO: Make this work for negative values
880
+ @marker_count = (@spread / @y_axis_increment).to_i
881
+ @increment = @y_axis_increment
1156
882
  end
1157
- # EMXIF
1158
883
  end
1159
884
 
885
+ # Used for degree => radian conversions
886
+ def deg2rad(angle)
887
+ angle * (Math::PI / 180.0)
888
+ end
1160
889
  end
1161
890
 
1162
- end # Magick
1163
-
1164
- class String
1165
- #Taken from http://codesnippets.joyent.com/posts/show/330
1166
- def commify(delimiter=',')
1167
- self.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
891
+ class IncorrectNumberOfDatasetsException < StandardError
1168
892
  end
1169
893
  end