gruff 0.11.0-java → 0.14.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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +66 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +24 -8
  5. data/.rubocop_todo.yml +57 -53
  6. data/CHANGELOG.md +34 -0
  7. data/README.md +15 -7
  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 -5
  12. data/lib/gruff.rb +9 -3
  13. data/lib/gruff/accumulator_bar.rb +3 -3
  14. data/lib/gruff/area.rb +5 -12
  15. data/lib/gruff/bar.rb +43 -47
  16. data/lib/gruff/base.rb +267 -146
  17. data/lib/gruff/bezier.rb +4 -10
  18. data/lib/gruff/bullet.rb +13 -19
  19. data/lib/gruff/dot.rb +14 -19
  20. data/lib/gruff/font.rb +39 -0
  21. data/lib/gruff/helper/bar_conversion.rb +28 -13
  22. data/lib/gruff/helper/bar_value_label.rb +68 -0
  23. data/lib/gruff/helper/stacked_mixin.rb +1 -2
  24. data/lib/gruff/histogram.rb +9 -8
  25. data/lib/gruff/line.rb +59 -56
  26. data/lib/gruff/mini/bar.rb +10 -7
  27. data/lib/gruff/mini/legend.rb +19 -10
  28. data/lib/gruff/mini/pie.rb +10 -8
  29. data/lib/gruff/mini/side_bar.rb +10 -8
  30. data/lib/gruff/net.rb +13 -20
  31. data/lib/gruff/patch/rmagick.rb +22 -24
  32. data/lib/gruff/patch/string.rb +7 -4
  33. data/lib/gruff/pie.rb +24 -70
  34. data/lib/gruff/renderer/bezier.rb +11 -11
  35. data/lib/gruff/renderer/circle.rb +11 -11
  36. data/lib/gruff/renderer/dash_line.rb +12 -12
  37. data/lib/gruff/renderer/dot.rb +16 -16
  38. data/lib/gruff/renderer/ellipse.rb +11 -11
  39. data/lib/gruff/renderer/line.rb +12 -12
  40. data/lib/gruff/renderer/polygon.rb +13 -13
  41. data/lib/gruff/renderer/polyline.rb +11 -11
  42. data/lib/gruff/renderer/rectangle.rb +9 -9
  43. data/lib/gruff/renderer/renderer.rb +23 -50
  44. data/lib/gruff/renderer/text.rb +32 -29
  45. data/lib/gruff/scatter.rb +52 -76
  46. data/lib/gruff/scene.rb +15 -14
  47. data/lib/gruff/side_bar.rb +49 -52
  48. data/lib/gruff/side_stacked_bar.rb +29 -20
  49. data/lib/gruff/spider.rb +13 -22
  50. data/lib/gruff/stacked_area.rb +10 -16
  51. data/lib/gruff/stacked_bar.rb +29 -18
  52. data/lib/gruff/store/{base_data.rb → basic_data.rb} +5 -7
  53. data/lib/gruff/store/custom_data.rb +4 -6
  54. data/lib/gruff/store/store.rb +9 -12
  55. data/lib/gruff/store/xy_data.rb +6 -7
  56. data/lib/gruff/themes.rb +6 -6
  57. data/lib/gruff/version.rb +1 -1
  58. data/rails_generators/gruff/templates/controller.rb +1 -1
  59. metadata +24 -14
  60. data/.travis.yml +0 -26
  61. data/Rakefile +0 -47
  62. data/assets/plastik/blue.png +0 -0
  63. data/assets/plastik/green.png +0 -0
  64. data/assets/plastik/red.png +0 -0
  65. data/docker/Dockerfile +0 -14
  66. data/docker/build.sh +0 -4
  67. data/docker/launch.sh +0 -4
  68. data/lib/gruff/helper/bar_value_label_mixin.rb +0 -30
  69. data/lib/gruff/photo_bar.rb +0 -97
@@ -1,21 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gruff
4
+ # @private
4
5
  class Renderer::Bezier
5
- def initialize(args = {})
6
- @color = args[:color]
7
- @width = args[:width] || 1.0
6
+ def initialize(renderer, color:, width: 1.0)
7
+ @renderer = renderer
8
+ @color = color
9
+ @width = width
8
10
  end
9
11
 
10
12
  def render(points)
11
- draw = Renderer.instance.draw
12
-
13
- draw.push
14
- draw.stroke(@color)
15
- draw.stroke_width(@width)
16
- draw.fill_opacity(0.0)
17
- draw.bezier(*points)
18
- draw.pop
13
+ @renderer.draw.push
14
+ @renderer.draw.stroke(@color)
15
+ @renderer.draw.stroke_width(@width)
16
+ @renderer.draw.fill_opacity(0.0)
17
+ @renderer.draw.bezier(*points)
18
+ @renderer.draw.pop
19
19
  end
20
20
  end
21
21
  end
@@ -1,21 +1,21 @@
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(renderer, color:, width: 1.0)
7
+ @renderer = renderer
8
+ @color = color
9
+ @width = width
8
10
  end
9
11
 
10
12
  def render(origin_x, origin_y, perim_x, perim_y)
11
- draw = Renderer.instance.draw
12
-
13
- draw.push
14
- draw.fill(@color)
15
- draw.stroke(@color)
16
- draw.stroke_width(@width)
17
- draw.circle(origin_x, origin_y, perim_x, perim_y)
18
- draw.pop
13
+ @renderer.draw.push
14
+ @renderer.draw.fill(@color)
15
+ @renderer.draw.stroke(@color)
16
+ @renderer.draw.stroke_width(@width)
17
+ @renderer.draw.circle(origin_x, origin_y, perim_x, perim_y)
18
+ @renderer.draw.pop
19
19
  end
20
20
  end
21
21
  end
@@ -1,22 +1,22 @@
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(renderer, color:, width:)
7
+ @renderer = renderer
8
+ @color = color
9
+ @width = width
8
10
  end
9
11
 
10
12
  def render(start_x, start_y, end_x, end_y)
11
- draw = Renderer.instance.draw
12
-
13
- draw.push
14
- draw.stroke_color(@color)
15
- draw.fill_opacity(0.0)
16
- draw.stroke_dasharray(10, 20)
17
- draw.stroke_width(@width)
18
- draw.line(start_x, start_y, end_x, end_y)
19
- draw.pop
13
+ @renderer.draw.push
14
+ @renderer.draw.stroke_color(@color)
15
+ @renderer.draw.fill_opacity(0.0)
16
+ @renderer.draw.stroke_dasharray(10, 20)
17
+ @renderer.draw.stroke_width(@width)
18
+ @renderer.draw.line(start_x, start_y, end_x, end_y)
19
+ @renderer.draw.pop
20
20
  end
21
21
  end
22
22
  end
@@ -1,39 +1,39 @@
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(renderer, style, color:, width: 1.0)
7
+ @renderer = renderer
6
8
  @style = style
7
- @color = config[:color]
8
- @width = config[:width] || 1.0
9
+ @color = color
10
+ @width = width
9
11
  end
10
12
 
11
13
  def render(new_x, new_y, circle_radius)
12
- draw = Renderer.instance.draw
13
-
14
- # draw.push # TODO
15
- draw.stroke_width(@width)
16
- draw.stroke(@color)
17
- draw.fill(@color)
14
+ # @renderer.draw.push # TODO
15
+ @renderer.draw.stroke_width(@width)
16
+ @renderer.draw.stroke(@color)
17
+ @renderer.draw.fill(@color)
18
18
  if @style.to_s == 'square'
19
- square(draw, new_x, new_y, circle_radius)
19
+ square(new_x, new_y, circle_radius)
20
20
  else
21
- circle(draw, new_x, new_y, circle_radius)
21
+ circle(new_x, new_y, circle_radius)
22
22
  end
23
- # draw.pop # TODO
23
+ # @renderer.draw.pop # TODO
24
24
  end
25
25
 
26
- def circle(draw, new_x, new_y, circle_radius)
27
- draw.circle(new_x, new_y, new_x - circle_radius, new_y)
26
+ def circle(new_x, new_y, circle_radius)
27
+ @renderer.draw.circle(new_x, new_y, new_x - circle_radius, new_y)
28
28
  end
29
29
 
30
- def square(draw, new_x, new_y, circle_radius)
30
+ def square(new_x, new_y, circle_radius)
31
31
  offset = (circle_radius * 0.8).to_i
32
32
  corner1 = new_x - offset
33
33
  corner2 = new_y - offset
34
34
  corner3 = new_x + offset
35
35
  corner4 = new_y + offset
36
- draw.rectangle(corner1, corner2, corner3, corner4)
36
+ @renderer.draw.rectangle(corner1, corner2, corner3, corner4)
37
37
  end
38
38
  end
39
39
  end
@@ -1,21 +1,21 @@
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(renderer, color:, width: 1.0)
7
+ @renderer = renderer
8
+ @color = color
9
+ @width = width
8
10
  end
9
11
 
10
12
  def render(origin_x, origin_y, width, height, arc_start, arc_end)
11
- draw = Renderer.instance.draw
12
-
13
- draw.push
14
- draw.stroke_width(@width)
15
- draw.stroke(@color)
16
- draw.fill('transparent')
17
- draw.ellipse(origin_x, origin_y, width, height, arc_start, arc_end)
18
- draw.pop
13
+ @renderer.draw.push
14
+ @renderer.draw.stroke_width(@width)
15
+ @renderer.draw.stroke(@color)
16
+ @renderer.draw.fill('transparent')
17
+ @renderer.draw.ellipse(origin_x, origin_y, width, height, arc_start, arc_end)
18
+ @renderer.draw.pop
19
19
  end
20
20
  end
21
21
  end
@@ -1,13 +1,15 @@
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
- @shadow_color = args[:shadow_color]
10
- @width = args[:width]
8
+ def initialize(renderer, color:, width: nil, shadow_color: nil)
9
+ @renderer = renderer
10
+ @color = color
11
+ @width = width
12
+ @shadow_color = shadow_color
11
13
  end
12
14
 
13
15
  def render(start_x, start_y, end_x, end_y)
@@ -29,14 +31,12 @@ module Gruff
29
31
  end_y += EPSILON
30
32
  end
31
33
 
32
- draw = Renderer.instance.draw
33
-
34
- draw.push
35
- draw.stroke(color)
36
- draw.fill(color)
37
- draw.stroke_width(@width) if @width
38
- draw.line(start_x, start_y, end_x, end_y)
39
- draw.pop
34
+ @renderer.draw.push
35
+ @renderer.draw.stroke(color)
36
+ @renderer.draw.fill(color)
37
+ @renderer.draw.stroke_width(@width) if @width
38
+ @renderer.draw.line(start_x, start_y, end_x, end_y)
39
+ @renderer.draw.pop
40
40
  end
41
41
  end
42
42
  end
@@ -1,23 +1,23 @@
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(renderer, color:, width: 1.0, opacity: 1.0)
7
+ @renderer = renderer
8
+ @color = color
9
+ @width = width
10
+ @opacity = opacity
9
11
  end
10
12
 
11
13
  def render(points)
12
- draw = Renderer.instance.draw
13
-
14
- draw.push
15
- draw.stroke_width(@width)
16
- draw.stroke(@color)
17
- draw.fill(@color)
18
- draw.fill_opacity(@opacity)
19
- draw.polygon(*points)
20
- draw.pop
14
+ @renderer.draw.push
15
+ @renderer.draw.stroke_width(@width)
16
+ @renderer.draw.stroke(@color)
17
+ @renderer.draw.fill(@color)
18
+ @renderer.draw.fill_opacity(@opacity)
19
+ @renderer.draw.polygon(*points)
20
+ @renderer.draw.pop
21
21
  end
22
22
  end
23
23
  end
@@ -1,21 +1,21 @@
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(renderer, color:, width:)
7
+ @renderer = renderer
8
+ @color = color
9
+ @width = width
8
10
  end
9
11
 
10
12
  def render(points)
11
- draw = Renderer.instance.draw
12
-
13
- draw.push
14
- draw.stroke(@color)
15
- draw.fill('transparent')
16
- draw.stroke_width(@width)
17
- draw.polyline(*points)
18
- draw.pop
13
+ @renderer.draw.push
14
+ @renderer.draw.stroke(@color)
15
+ @renderer.draw.fill('transparent')
16
+ @renderer.draw.stroke_width(@width)
17
+ @renderer.draw.polyline(*points)
18
+ @renderer.draw.pop
19
19
  end
20
20
  end
21
21
  end
@@ -1,19 +1,19 @@
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(renderer, color: nil)
7
+ @renderer = renderer
8
+ @color = color
7
9
  end
8
10
 
9
11
  def render(upper_left_x, upper_left_y, lower_right_x, lower_right_y)
10
- draw = Renderer.instance.draw
11
-
12
- draw.push
13
- draw.stroke('transparent')
14
- draw.fill(@color) if @color
15
- draw.rectangle(upper_left_x, upper_left_y, lower_right_x, lower_right_y)
16
- draw.pop
12
+ @renderer.draw.push
13
+ @renderer.draw.stroke('transparent')
14
+ @renderer.draw.fill(@color) if @color
15
+ @renderer.draw.rectangle(upper_left_x, upper_left_y, lower_right_x, lower_right_y)
16
+ @renderer.draw.pop
17
17
  end
18
18
  end
19
19
  end
@@ -1,69 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'singleton'
4
-
5
3
  module Gruff
6
4
  # @private
7
5
  class Renderer
8
- include Singleton
9
-
10
- attr_accessor :draw, :image, :scale, :text_renderers
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)
18
-
19
- image = Renderer.instance.background(columns, rows, scale, theme_options)
20
-
21
- Renderer.instance.draw = draw
22
- Renderer.instance.scale = scale
23
- Renderer.instance.image = image
24
- Renderer.instance.text_renderers = []
25
- end
6
+ attr_accessor :text_renderers
7
+ attr_reader :draw, :image, :scale
26
8
 
27
- def setup_transparent_background(columns, rows)
28
- image = Renderer.instance.render_transparent_background(columns, rows)
29
- Renderer.instance.image = image
30
- end
31
-
32
- def background_image=(image)
33
- Renderer.instance.image = image
34
- end
35
-
36
- def font=(font)
37
- draw = Renderer.instance.draw
38
- draw.font = font if font
39
- end
9
+ def initialize(columns, rows, scale, theme_options)
10
+ @draw = Magick::Draw.new
11
+ @text_renderers = []
40
12
 
41
- def finish
42
- draw = Renderer.instance.draw
43
- image = Renderer.instance.image
13
+ @scale = scale
14
+ @draw.scale(scale, scale)
15
+ @image = background(columns, rows, scale, theme_options)
16
+ end
44
17
 
45
- draw.draw(image)
18
+ def finish
19
+ @draw.draw(@image)
46
20
 
47
- Renderer.instance.text_renderers.each do |renderer|
48
- renderer.render(renderer.width, renderer.height, renderer.x, renderer.y, renderer.gravity)
49
- end
50
- end
51
-
52
- def write(file_name)
53
- Renderer.instance.image.write(file_name)
21
+ @text_renderers.each do |renderer|
22
+ renderer.render(renderer.width, renderer.height, renderer.x, renderer.y, renderer.gravity)
54
23
  end
24
+ end
55
25
 
56
- def to_blob(file_format)
57
- Renderer.instance.image.to_blob do
58
- self.format = file_format
59
- end
60
- end
26
+ def background_image=(image)
27
+ @image = image
61
28
  end
62
29
 
63
30
  def background(columns, rows, scale, theme_options)
64
31
  case theme_options[:background_colors]
65
32
  when Array
66
- gradated_background(columns, rows, theme_options[:background_colors][0], theme_options[:background_colors][1], theme_options[:background_direction])
33
+ gradated_background(columns, rows, *theme_options[:background_colors][0..1], theme_options[:background_direction])
67
34
  when String
68
35
  solid_background(columns, rows, theme_options[:background_colors])
69
36
  else
@@ -71,6 +38,10 @@ module Gruff
71
38
  end
72
39
  end
73
40
 
41
+ def transparent_background(columns, rows)
42
+ @image = render_transparent_background(columns, rows)
43
+ end
44
+
74
45
  # Make a new image at the current size with a solid +color+.
75
46
  def solid_background(columns, rows, color)
76
47
  Magick::Image.new(columns, rows) do
@@ -113,6 +84,8 @@ module Gruff
113
84
  end
114
85
  end
115
86
 
87
+ private
88
+
116
89
  # Use with a theme to use an image (800x600 original) background.
117
90
  def image_background(scale, image_path)
118
91
  image = Magick::Image.read(image_path)
@@ -1,17 +1,19 @@
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(renderer, text, font:, rotation: nil)
9
+ @renderer = renderer
6
10
  @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]
11
+ @font = font
12
+ @rotation = rotation
12
13
  end
13
14
 
14
15
  attr_reader :width, :height, :x, :y, :gravity
16
+
15
17
  def add_to_render_queue(width, height, x, y, gravity = Magick::NorthGravity)
16
18
  @width = width
17
19
  @height = height
@@ -19,35 +21,36 @@ module Gruff
19
21
  @y = y
20
22
  @gravity = gravity
21
23
 
22
- Renderer.instance.text_renderers << self
24
+ @renderer.text_renderers << self
23
25
  end
24
26
 
25
27
  def render(width, height, x, y, gravity = Magick::NorthGravity)
26
- draw = Renderer.instance.draw
27
- image = Renderer.instance.image
28
- scale = Renderer.instance.scale
29
-
30
- draw.rotation = @rotation if @rotation
31
- draw.fill = @font_color
32
- draw.stroke = 'transparent'
33
- draw.font = @font if @font
34
- draw.font_weight = @font_weight
35
- draw.pointsize = @font_size * scale
36
- draw.gravity = gravity
37
- draw.annotate_scaled(image,
38
- width, height,
39
- x, y,
40
- @text, scale)
41
- draw.rotation = -@rotation if @rotation
28
+ @renderer.draw.rotation = @rotation if @rotation
29
+ @renderer.draw.fill = @font.color
30
+ @renderer.draw.stroke = 'transparent'
31
+ @renderer.draw.font = @font.file_path
32
+ @renderer.draw.font_weight = @font.weight
33
+ @renderer.draw.pointsize = @font.size * @renderer.scale
34
+ @renderer.draw.gravity = gravity
35
+ @renderer.draw.annotate_scaled(@renderer.image,
36
+ width, height,
37
+ x, y,
38
+ @text, @renderer.scale)
39
+ @renderer.draw.rotation = -@rotation if @rotation
42
40
  end
43
41
 
44
- def self.metrics(text, size, font_weight = Magick::NormalWeight)
45
- draw = Renderer.instance.draw
46
- image = Renderer.instance.image
42
+ def metrics
43
+ @renderer.draw.font = @font.file_path
44
+ @renderer.draw.font_weight = @font.weight
45
+ @renderer.draw.pointsize = @font.size
46
+
47
+ # The old ImageMagick causes SEGV with string which has '%' + alphabet (eg. '%S').
48
+ # This format is used to embed value into a string using image properties.
49
+ # However, gruff use plain image as canvas which does not have any property.
50
+ # So, in here, it just escape % in order to avoid SEGV.
51
+ text = @text.to_s.gsub(/(%+)/) { ('%' * Regexp.last_match(1).size * 2).to_s }
47
52
 
48
- draw.font_weight = font_weight
49
- draw.pointsize = size
50
- draw.get_type_metrics(image, text.to_s)
53
+ @renderer.draw.get_type_metrics(@renderer.image, text)
51
54
  end
52
55
  end
53
56
  end