gruff 0.10.0-java → 0.13.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +24 -4
  4. data/.rubocop_todo.yml +94 -42
  5. data/.travis.yml +3 -6
  6. data/CHANGELOG.md +35 -0
  7. data/README.md +10 -1
  8. data/assets/fonts/LICENSE.txt +202 -0
  9. data/assets/fonts/Roboto-Bold.ttf +0 -0
  10. data/assets/fonts/Roboto-Regular.ttf +0 -0
  11. data/gruff.gemspec +8 -3
  12. data/lib/gruff.rb +8 -3
  13. data/lib/gruff/accumulator_bar.rb +0 -2
  14. data/lib/gruff/area.rb +2 -6
  15. data/lib/gruff/bar.rb +35 -35
  16. data/lib/gruff/base.rb +295 -188
  17. data/lib/gruff/bezier.rb +0 -4
  18. data/lib/gruff/bullet.rb +12 -14
  19. data/lib/gruff/dot.rb +8 -33
  20. data/lib/gruff/helper/bar_conversion.rb +34 -19
  21. data/lib/gruff/helper/bar_value_label.rb +68 -0
  22. data/lib/gruff/histogram.rb +25 -25
  23. data/lib/gruff/line.rb +29 -26
  24. data/lib/gruff/mini/bar.rb +1 -1
  25. data/lib/gruff/mini/legend.rb +9 -4
  26. data/lib/gruff/mini/pie.rb +1 -2
  27. data/lib/gruff/mini/side_bar.rb +1 -2
  28. data/lib/gruff/net.rb +19 -20
  29. data/lib/gruff/patch/rmagick.rb +22 -24
  30. data/lib/gruff/patch/string.rb +7 -4
  31. data/lib/gruff/photo_bar.rb +12 -16
  32. data/lib/gruff/pie.rb +19 -30
  33. data/lib/gruff/renderer/bezier.rb +4 -3
  34. data/lib/gruff/renderer/circle.rb +4 -3
  35. data/lib/gruff/renderer/dash_line.rb +4 -3
  36. data/lib/gruff/renderer/dot.rb +4 -3
  37. data/lib/gruff/renderer/ellipse.rb +4 -3
  38. data/lib/gruff/renderer/line.rb +14 -5
  39. data/lib/gruff/renderer/polygon.rb +5 -4
  40. data/lib/gruff/renderer/polyline.rb +4 -3
  41. data/lib/gruff/renderer/rectangle.rb +3 -2
  42. data/lib/gruff/renderer/renderer.rb +31 -38
  43. data/lib/gruff/renderer/text.rb +39 -9
  44. data/lib/gruff/scatter.rb +30 -44
  45. data/lib/gruff/scene.rb +0 -1
  46. data/lib/gruff/side_bar.rb +60 -45
  47. data/lib/gruff/side_stacked_bar.rb +30 -19
  48. data/lib/gruff/spider.rb +18 -17
  49. data/lib/gruff/stacked_area.rb +8 -7
  50. data/lib/gruff/stacked_bar.rb +28 -18
  51. data/lib/gruff/store/{base_data.rb → basic_data.rb} +9 -7
  52. data/lib/gruff/store/custom_data.rb +8 -6
  53. data/lib/gruff/store/store.rb +6 -5
  54. data/lib/gruff/store/xy_data.rb +10 -7
  55. data/lib/gruff/version.rb +1 -1
  56. metadata +36 -9
  57. data/Rakefile +0 -23
  58. data/docker/Dockerfile +0 -14
  59. data/docker/build.sh +0 -4
  60. data/docker/launch.sh +0 -4
  61. data/lib/gruff/helper/bar_value_label_mixin.rb +0 -30
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gruff
4
+ # @private
4
5
  class Renderer::Circle
5
- def initialize(args = {})
6
- @color = args[:color]
7
- @width = args[:width] || 1.0
6
+ def initialize(color:, width: 1.0)
7
+ @color = color
8
+ @width = width
8
9
  end
9
10
 
10
11
  def render(origin_x, origin_y, perim_x, perim_y)
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gruff
4
+ # @private
4
5
  class Renderer::DashLine
5
- def initialize(args = {})
6
- @color = args[:color]
7
- @width = args[:width]
6
+ def initialize(color:, width:)
7
+ @color = color
8
+ @width = width
8
9
  end
9
10
 
10
11
  def render(start_x, start_y, end_x, end_y)
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gruff
4
+ # @private
4
5
  class Renderer::Dot
5
- def initialize(style, config)
6
+ def initialize(style, color:, width: 1.0)
6
7
  @style = style
7
- @color = config[:color]
8
- @width = config[:width] || 1.0
8
+ @color = color
9
+ @width = width
9
10
  end
10
11
 
11
12
  def render(new_x, new_y, circle_radius)
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gruff
4
+ # @private
4
5
  class Renderer::Ellipse
5
- def initialize(args = {})
6
- @color = args[:color]
7
- @width = args[:width] || 1.0
6
+ def initialize(color:, width: 1.0)
7
+ @color = color
8
+ @width = width
8
9
  end
9
10
 
10
11
  def render(origin_x, origin_y, width, height, arc_start, arc_end)
@@ -1,15 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gruff
4
+ # @private
4
5
  class Renderer::Line
5
6
  EPSILON = 0.001
6
7
 
7
- def initialize(args = {})
8
- @color = args[:color]
9
- @width = args[:width]
8
+ def initialize(color:, width: nil, shadow_color: nil)
9
+ @color = color
10
+ @width = width
11
+ @shadow_color = shadow_color
10
12
  end
11
13
 
12
14
  def render(start_x, start_y, end_x, end_y)
15
+ render_line(start_x, start_y, end_x, end_y, @color)
16
+ render_line(start_x, start_y + 1, end_x, end_y + 1, @shadow_color) if @shadow_color
17
+ end
18
+
19
+ private
20
+
21
+ def render_line(start_x, start_y, end_x, end_y, color)
13
22
  # FIXME(uwe): Workaround for Issue #66
14
23
  # https://github.com/topfunky/gruff/issues/66
15
24
  # https://github.com/rmagick/rmagick/issues/82
@@ -24,8 +33,8 @@ module Gruff
24
33
  draw = Renderer.instance.draw
25
34
 
26
35
  draw.push
27
- draw.stroke(@color)
28
- draw.fill(@color)
36
+ draw.stroke(color)
37
+ draw.fill(color)
29
38
  draw.stroke_width(@width) if @width
30
39
  draw.line(start_x, start_y, end_x, end_y)
31
40
  draw.pop
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gruff
4
+ # @private
4
5
  class Renderer::Polygon
5
- def initialize(args = {})
6
- @color = args[:color]
7
- @width = args[:width] || 1.0
8
- @opacity = args[:opacity] || 1.0
6
+ def initialize(color:, width: 1.0, opacity: 1.0)
7
+ @color = color
8
+ @width = width
9
+ @opacity = opacity
9
10
  end
10
11
 
11
12
  def render(points)
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gruff
4
+ # @private
4
5
  class Renderer::Polyline
5
- def initialize(args = {})
6
- @color = args[:color]
7
- @width = args[:width]
6
+ def initialize(color:, width:)
7
+ @color = color
8
+ @width = width
8
9
  end
9
10
 
10
11
  def render(points)
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gruff
4
+ # @private
4
5
  class Renderer::Rectangle
5
- def initialize(args = {})
6
- @color = args[:color]
6
+ def initialize(color: nil)
7
+ @color = color
7
8
  end
8
9
 
9
10
  def render(upper_left_x, upper_left_y, lower_right_x, lower_right_y)
@@ -7,58 +7,51 @@ module Gruff
7
7
  class Renderer
8
8
  include Singleton
9
9
 
10
- attr_accessor :draw, :image, :scale
10
+ attr_accessor :draw, :image, :scale, :text_renderers
11
11
 
12
- class << self
13
- def setup(columns, rows, font, scale, theme_options)
14
- draw = Magick::Draw.new
15
- draw.font = font if font
16
- # Scale down from 800x600 used to calculate drawing.
17
- draw.scale(scale, scale)
12
+ def self.setup(columns, rows, font, scale, theme_options)
13
+ draw = Magick::Draw.new
14
+ draw.font = font if font
15
+ # Scale down from 800x600 used to calculate drawing.
16
+ draw.scale(scale, scale)
18
17
 
19
- image = Renderer.instance.background(columns, rows, scale, theme_options)
18
+ image = Renderer.instance.background(columns, rows, scale, theme_options)
20
19
 
21
- Renderer.instance.draw = draw
22
- Renderer.instance.scale = scale
23
- Renderer.instance.image = image
24
- end
25
-
26
- def setup_transparent_background(columns, rows)
27
- image = Renderer.instance.render_transparent_background(columns, rows)
28
- Renderer.instance.image = image
29
- end
20
+ Renderer.instance.draw = draw
21
+ Renderer.instance.scale = scale
22
+ Renderer.instance.image = image
23
+ Renderer.instance.text_renderers = []
24
+ end
30
25
 
31
- def background_image=(image)
32
- Renderer.instance.image = image
33
- end
26
+ def self.setup_transparent_background(columns, rows)
27
+ image = Renderer.instance.render_transparent_background(columns, rows)
28
+ Renderer.instance.image = image
29
+ end
34
30
 
35
- def font=(font)
36
- draw = Renderer.instance.draw
37
- draw.font = font if font
38
- end
31
+ def self.background_image=(image)
32
+ Renderer.instance.image = image
33
+ end
39
34
 
40
- def finish
41
- draw = Renderer.instance.draw
42
- image = Renderer.instance.image
35
+ def self.font=(font)
36
+ draw = Renderer.instance.draw
37
+ draw.font = font if font
38
+ end
43
39
 
44
- draw.draw(image)
45
- end
40
+ def self.finish
41
+ draw = Renderer.instance.draw
42
+ image = Renderer.instance.image
46
43
 
47
- def write(file_name)
48
- Renderer.instance.image.write(file_name)
49
- end
44
+ draw.draw(image)
50
45
 
51
- def to_blob(file_format)
52
- Renderer.instance.image.to_blob do
53
- self.format = file_format
54
- end
46
+ Renderer.instance.text_renderers.each do |renderer|
47
+ renderer.render(renderer.width, renderer.height, renderer.x, renderer.y, renderer.gravity)
55
48
  end
56
49
  end
57
50
 
58
51
  def background(columns, rows, scale, theme_options)
59
52
  case theme_options[:background_colors]
60
53
  when Array
61
- gradated_background(columns, rows, theme_options[:background_colors][0], theme_options[:background_colors][1], theme_options[:background_direction])
54
+ gradated_background(columns, rows, *theme_options[:background_colors][0..1], theme_options[:background_direction])
62
55
  when String
63
56
  solid_background(columns, rows, theme_options[:background_colors])
64
57
  else
@@ -102,7 +95,7 @@ module Gruff
102
95
 
103
96
  if @gradated_background_retry_count < 3
104
97
  @gradated_background_retry_count += 1
105
- gradated_background(top_color, bottom_color, direct)
98
+ gradated_background(columns, rows, top_color, bottom_color, direct)
106
99
  else
107
100
  raise e
108
101
  end
@@ -1,14 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gruff
4
+ # @private
4
5
  class Renderer::Text
5
- def initialize(text, args = {})
6
+ using Magick::GruffAnnotate
7
+
8
+ def initialize(text, font:, size:, color:, weight: Magick::NormalWeight, rotation: nil)
6
9
  @text = text.to_s
7
- @font = args[:font]
8
- @font_size = args[:size]
9
- @font_color = args[:color]
10
- @font_weight = args[:weight] || Magick::NormalWeight
11
- @rotation = args[:rotation]
10
+ @font = font
11
+ @font_size = size
12
+ @font_color = color
13
+ @font_weight = weight
14
+ @rotation = rotation
15
+ end
16
+
17
+ attr_reader :width, :height, :x, :y, :gravity
18
+
19
+ def add_to_render_queue(width, height, x, y, gravity = Magick::NorthGravity)
20
+ @width = width
21
+ @height = height
22
+ @x = x
23
+ @y = y
24
+ @gravity = gravity
25
+
26
+ Renderer.instance.text_renderers << self
12
27
  end
13
28
 
14
29
  def render(width, height, x, y, gravity = Magick::NorthGravity)
@@ -19,7 +34,7 @@ module Gruff
19
34
  draw.rotation = @rotation if @rotation
20
35
  draw.fill = @font_color
21
36
  draw.stroke = 'transparent'
22
- draw.font = @font if @font
37
+ draw.font = @font || Renderer::Text.default_font(@font_weight)
23
38
  draw.font_weight = @font_weight
24
39
  draw.pointsize = @font_size * scale
25
40
  draw.gravity = gravity
@@ -30,13 +45,28 @@ module Gruff
30
45
  draw.rotation = -@rotation if @rotation
31
46
  end
32
47
 
33
- def self.metrics(text, size, font_weight = Magick::NormalWeight)
48
+ def self.metrics(text, font, size, font_weight = Magick::NormalWeight)
34
49
  draw = Renderer.instance.draw
35
50
  image = Renderer.instance.image
36
51
 
52
+ draw.font = font || Renderer::Text.default_font(font_weight)
37
53
  draw.font_weight = font_weight
38
54
  draw.pointsize = size
39
- draw.get_type_metrics(image, text.to_s)
55
+
56
+ # The old ImageMagick causes SEGV with string which has '%' + alphabet (eg. '%S').
57
+ # This format is used to embed value into a string using image properties.
58
+ # However, gruff use plain image as canvas which does not have any property.
59
+ # So, in here, it just escape % in order to avoid SEGV.
60
+ text = text.to_s.gsub(/(%+)/) { ('%' * Regexp.last_match(1).size * 2).to_s }
61
+
62
+ draw.get_type_metrics(image, text)
63
+ end
64
+
65
+ FONT_BOLD = File.expand_path(File.join(__FILE__, '../../../../assets/fonts/Roboto-Bold.ttf'))
66
+ FONT_REGULAR = File.expand_path(File.join(__FILE__, '../../../../assets/fonts/Roboto-Regular.ttf'))
67
+
68
+ def self.default_font(font_weight)
69
+ (font_weight == Magick::BoldWeight) ? FONT_BOLD : FONT_REGULAR
40
70
  end
41
71
  end
42
72
  end
data/lib/gruff/scatter.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'gruff/base'
4
-
5
3
  #
6
4
  # Here's how to set up a Gruff::Scatter.
7
5
  #
@@ -13,33 +11,34 @@ require 'gruff/base'
13
11
  class Gruff::Scatter < Gruff::Base
14
12
  # Maximum X Value. The value will get overwritten by the max in the
15
13
  # datasets.
16
- attr_accessor :maximum_x_value
14
+ attr_writer :maximum_x_value
17
15
 
18
16
  # Minimum X Value. The value will get overwritten by the min in the
19
17
  # datasets.
20
- attr_accessor :minimum_x_value
18
+ attr_writer :minimum_x_value
21
19
 
22
20
  # The number of vertical lines shown for reference.
23
- attr_accessor :marker_x_count
21
+ attr_writer :marker_x_count
24
22
 
25
23
  # Attributes to allow customising the size of the points.
26
- attr_accessor :circle_radius
27
- attr_accessor :stroke_width
24
+ attr_writer :circle_radius
25
+ attr_writer :stroke_width
28
26
 
29
27
  # Allow disabling the significant rounding when labeling the X axis.
30
28
  # This is useful when working with a small range of high values (for example, a date range of months, while seconds as units).
31
- attr_accessor :disable_significant_rounding_x_axis
29
+ attr_writer :disable_significant_rounding_x_axis
32
30
 
33
- # Allow enabling vertical lines. When you have a lot of data, they can work great.
34
- attr_accessor :enable_vertical_line_markers
31
+ # Allow for vertical marker lines.
32
+ attr_writer :show_vertical_markers
35
33
 
36
34
  # Allow using vertical labels in the X axis (and setting the label margin).
37
- attr_accessor :x_label_margin
38
- attr_accessor :use_vertical_x_labels
35
+ attr_writer :x_label_margin
36
+ attr_writer :use_vertical_x_labels
39
37
 
40
- # Allow passing lambdas to format labels.
41
- attr_accessor :y_axis_label_format
42
- attr_accessor :x_axis_label_format
38
+ def initialize_store
39
+ @store = Gruff::Store.new(Gruff::Store::XYData)
40
+ end
41
+ private :initialize_store
43
42
 
44
43
  def initialize_ivars
45
44
  super
@@ -48,19 +47,22 @@ class Gruff::Scatter < Gruff::Base
48
47
  @baseline_x_value = @baseline_y_value = nil
49
48
  @circle_radius = nil
50
49
  @disable_significant_rounding_x_axis = false
51
- @enable_vertical_line_markers = false
50
+ @show_vertical_markers = false
52
51
  @marker_x_count = nil
53
52
  @maximum_x_value = @minimum_x_value = nil
54
53
  @stroke_width = nil
55
54
  @use_vertical_x_labels = false
56
- @x_axis_label_format = nil
57
55
  @x_label_margin = nil
58
- @y_axis_label_format = nil
59
-
60
- @store = Gruff::Store.new(Gruff::Store::XYData)
61
56
  end
62
57
  private :initialize_ivars
63
58
 
59
+ # Allow enabling vertical lines. When you have a lot of data, they can work great.
60
+ # @deprecated Please use +show_vertical_markers+ attribute instead.
61
+ def enable_vertical_line_markers=(value)
62
+ warn '#enable_vertical_line_markers= is deprecated. Please use `show_vertical_markers` attribute instead'
63
+ @show_vertical_markers = value
64
+ end
65
+
64
66
  def draw
65
67
  super
66
68
  return unless data_given?
@@ -81,8 +83,6 @@ class Gruff::Scatter < Gruff::Base
81
83
  Gruff::Renderer::Circle.new(color: data_row.color, width: stroke_width).render(new_x, new_y, new_x - circle_radius, new_y)
82
84
  end
83
85
  end
84
-
85
- Gruff::Renderer.finish
86
86
  end
87
87
 
88
88
  # The first parameter is the name of the dataset. The next two are the
@@ -192,7 +192,7 @@ private
192
192
  calculate_spread
193
193
  normalize
194
194
 
195
- @marker_count = (@x_spread / @x_axis_increment).to_i
195
+ @marker_x_count = (@x_spread / @x_axis_increment).to_i
196
196
  @x_increment = @x_axis_increment
197
197
  end
198
198
  increment_x_scaled = @graph_width.to_f / (@x_spread / @x_increment)
@@ -200,40 +200,26 @@ private
200
200
  # Draw vertical line markers and annotate with numbers
201
201
  (0..@marker_x_count).each do |index|
202
202
  # TODO: Fix the vertical lines, and enable them by default. Not pretty when they don't match up with top y-axis line
203
- if @enable_vertical_line_markers
203
+ if @show_vertical_markers
204
204
  x = @graph_left + @graph_width - index.to_f * increment_x_scaled
205
- Gruff::Renderer::Line.new(color: @marker_color).render(x, @graph_top, x, @graph_bottom)
205
+
206
+ line_renderer = Gruff::Renderer::Line.new(color: @marker_color, shadow_color: @marker_shadow_color)
207
+ line_renderer.render(x, @graph_top, x, @graph_bottom)
206
208
  end
207
209
 
208
210
  unless @hide_line_numbers
209
- marker_label = index * @x_increment + @minimum_x_value.to_f
211
+ marker_label = BigDecimal(index.to_s) * BigDecimal(@x_increment.to_s) + BigDecimal(@minimum_x_value.to_s)
210
212
  y_offset = @graph_bottom + (@x_label_margin || LABEL_MARGIN)
211
213
  x_offset = get_x_coord(index.to_f, increment_x_scaled, @graph_left)
212
214
 
213
- label = vertical_label(marker_label, @x_increment)
215
+ label = x_axis_label(marker_label, @x_increment)
214
216
  rotation = -90.0 if @use_vertical_x_labels
215
217
  text_renderer = Gruff::Renderer::Text.new(label, font: @font, size: @marker_font_size, color: @font_color, rotation: rotation)
216
- text_renderer.render(1.0, 1.0, x_offset, y_offset)
218
+ text_renderer.add_to_render_queue(1.0, 1.0, x_offset, y_offset)
217
219
  end
218
220
  end
219
221
  end
220
222
 
221
- def label(value, increment)
222
- if @y_axis_label_format
223
- @y_axis_label_format.call(value)
224
- else
225
- super
226
- end
227
- end
228
-
229
- def vertical_label(value, increment)
230
- if @x_axis_label_format
231
- @x_axis_label_format.call(value)
232
- else
233
- label(value, increment)
234
- end
235
- end
236
-
237
223
  def get_x_coord(x_data_point, width, offset) #:nodoc:
238
224
  x_data_point * width + offset
239
225
  end