gruff 0.7.0-java → 0.12.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +5 -5
  2. data/{History.txt → CHANGELOG.md} +81 -25
  3. data/Gemfile +3 -1
  4. data/README.md +51 -23
  5. data/assets/plastik/blue.png +0 -0
  6. data/assets/plastik/green.png +0 -0
  7. data/assets/plastik/red.png +0 -0
  8. data/gruff.gemspec +23 -13
  9. data/init.rb +2 -0
  10. data/lib/gruff.rb +33 -4
  11. data/lib/gruff/accumulator_bar.rb +17 -9
  12. data/lib/gruff/area.rb +31 -21
  13. data/lib/gruff/bar.rb +90 -46
  14. data/lib/gruff/base.rb +476 -710
  15. data/lib/gruff/bezier.rb +29 -18
  16. data/lib/gruff/bullet.rb +58 -71
  17. data/lib/gruff/dot.rb +35 -83
  18. data/lib/gruff/helper/bar_conversion.rb +47 -0
  19. data/lib/gruff/helper/bar_value_label_mixin.rb +33 -0
  20. data/lib/gruff/helper/stacked_mixin.rb +23 -0
  21. data/lib/gruff/histogram.rb +59 -0
  22. data/lib/gruff/line.rb +121 -199
  23. data/lib/gruff/mini/bar.rb +17 -10
  24. data/lib/gruff/mini/legend.rb +26 -38
  25. data/lib/gruff/mini/pie.rb +18 -13
  26. data/lib/gruff/mini/side_bar.rb +25 -12
  27. data/lib/gruff/net.rb +69 -83
  28. data/lib/gruff/patch/rmagick.rb +31 -0
  29. data/lib/gruff/patch/string.rb +13 -0
  30. data/lib/gruff/photo_bar.rb +36 -43
  31. data/lib/gruff/pie.rb +42 -103
  32. data/lib/gruff/renderer/bezier.rb +22 -0
  33. data/lib/gruff/renderer/circle.rb +22 -0
  34. data/lib/gruff/renderer/dash_line.rb +23 -0
  35. data/lib/gruff/renderer/dot.rb +40 -0
  36. data/lib/gruff/renderer/ellipse.rb +22 -0
  37. data/lib/gruff/renderer/line.rb +43 -0
  38. data/lib/gruff/renderer/polygon.rb +24 -0
  39. data/lib/gruff/renderer/polyline.rb +22 -0
  40. data/lib/gruff/renderer/rectangle.rb +20 -0
  41. data/lib/gruff/renderer/renderer.rb +120 -0
  42. data/lib/gruff/renderer/text.rb +57 -0
  43. data/lib/gruff/scatter.rb +128 -201
  44. data/lib/gruff/scene.rb +30 -41
  45. data/lib/gruff/side_bar.rb +100 -68
  46. data/lib/gruff/side_stacked_bar.rb +92 -63
  47. data/lib/gruff/spider.rb +47 -53
  48. data/lib/gruff/stacked_area.rb +37 -34
  49. data/lib/gruff/stacked_bar.rb +99 -54
  50. data/lib/gruff/store/basic_data.rb +36 -0
  51. data/lib/gruff/store/custom_data.rb +36 -0
  52. data/lib/gruff/store/store.rb +81 -0
  53. data/lib/gruff/store/xy_data.rb +58 -0
  54. data/lib/gruff/themes.rb +32 -33
  55. data/lib/gruff/version.rb +3 -1
  56. metadata +74 -102
  57. data/.gitignore +0 -7
  58. data/.travis.yml +0 -19
  59. data/Manifest.txt +0 -81
  60. data/RELEASE.md +0 -30
  61. data/Rakefile +0 -218
  62. data/assets/bubble.png +0 -0
  63. data/assets/city_scene/background/0000.png +0 -0
  64. data/assets/city_scene/background/0600.png +0 -0
  65. data/assets/city_scene/background/2000.png +0 -0
  66. data/assets/city_scene/clouds/cloudy.png +0 -0
  67. data/assets/city_scene/clouds/partly_cloudy.png +0 -0
  68. data/assets/city_scene/clouds/stormy.png +0 -0
  69. data/assets/city_scene/grass/default.png +0 -0
  70. data/assets/city_scene/haze/true.png +0 -0
  71. data/assets/city_scene/number_sample/1.png +0 -0
  72. data/assets/city_scene/number_sample/2.png +0 -0
  73. data/assets/city_scene/number_sample/default.png +0 -0
  74. data/assets/city_scene/sky/0000.png +0 -0
  75. data/assets/city_scene/sky/0200.png +0 -0
  76. data/assets/city_scene/sky/0400.png +0 -0
  77. data/assets/city_scene/sky/0600.png +0 -0
  78. data/assets/city_scene/sky/0800.png +0 -0
  79. data/assets/city_scene/sky/1000.png +0 -0
  80. data/assets/city_scene/sky/1200.png +0 -0
  81. data/assets/city_scene/sky/1400.png +0 -0
  82. data/assets/city_scene/sky/1500.png +0 -0
  83. data/assets/city_scene/sky/1700.png +0 -0
  84. data/assets/city_scene/sky/2000.png +0 -0
  85. data/assets/pc306715.jpg +0 -0
  86. data/lib/gruff/bar_conversion.rb +0 -46
  87. data/lib/gruff/deprecated.rb +0 -39
  88. data/lib/gruff/stacked_mixin.rb +0 -23
  89. data/test/gruff_test_case.rb +0 -152
  90. data/test/image_compare.rb +0 -58
  91. data/test/test_accumulator_bar.rb +0 -51
  92. data/test/test_area.rb +0 -134
  93. data/test/test_bar.rb +0 -505
  94. data/test/test_base.rb +0 -33
  95. data/test/test_bezier.rb +0 -33
  96. data/test/test_bullet.rb +0 -26
  97. data/test/test_dot.rb +0 -263
  98. data/test/test_labels_for_null_data.rb +0 -27
  99. data/test/test_legend.rb +0 -68
  100. data/test/test_line.rb +0 -674
  101. data/test/test_mini_bar.rb +0 -33
  102. data/test/test_mini_pie.rb +0 -25
  103. data/test/test_mini_side_bar.rb +0 -36
  104. data/test/test_net.rb +0 -231
  105. data/test/test_photo.rb +0 -41
  106. data/test/test_pie.rb +0 -194
  107. data/test/test_scatter.rb +0 -270
  108. data/test/test_scene.rb +0 -100
  109. data/test/test_side_bar.rb +0 -56
  110. data/test/test_sidestacked_bar.rb +0 -105
  111. data/test/test_spider.rb +0 -226
  112. data/test/test_stacked_area.rb +0 -52
  113. data/test/test_stacked_bar.rb +0 -68
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gruff
4
+ # @private
5
+ class Renderer::DashLine
6
+ def initialize(color:, width:)
7
+ @color = color
8
+ @width = width
9
+ end
10
+
11
+ def render(start_x, start_y, end_x, end_y)
12
+ draw = Renderer.instance.draw
13
+
14
+ draw.push
15
+ draw.stroke_color(@color)
16
+ draw.fill_opacity(0.0)
17
+ draw.stroke_dasharray(10, 20)
18
+ draw.stroke_width(@width)
19
+ draw.line(start_x, start_y, end_x, end_y)
20
+ draw.pop
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gruff
4
+ # @private
5
+ class Renderer::Dot
6
+ def initialize(style, color:, width: 1.0)
7
+ @style = style
8
+ @color = color
9
+ @width = width
10
+ end
11
+
12
+ def render(new_x, new_y, circle_radius)
13
+ draw = Renderer.instance.draw
14
+
15
+ # draw.push # TODO
16
+ draw.stroke_width(@width)
17
+ draw.stroke(@color)
18
+ draw.fill(@color)
19
+ if @style.to_s == 'square'
20
+ square(draw, new_x, new_y, circle_radius)
21
+ else
22
+ circle(draw, new_x, new_y, circle_radius)
23
+ end
24
+ # draw.pop # TODO
25
+ end
26
+
27
+ def circle(draw, new_x, new_y, circle_radius)
28
+ draw.circle(new_x, new_y, new_x - circle_radius, new_y)
29
+ end
30
+
31
+ def square(draw, new_x, new_y, circle_radius)
32
+ offset = (circle_radius * 0.8).to_i
33
+ corner1 = new_x - offset
34
+ corner2 = new_y - offset
35
+ corner3 = new_x + offset
36
+ corner4 = new_y + offset
37
+ draw.rectangle(corner1, corner2, corner3, corner4)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gruff
4
+ # @private
5
+ class Renderer::Ellipse
6
+ def initialize(color:, width: 1.0)
7
+ @color = color
8
+ @width = width
9
+ end
10
+
11
+ def render(origin_x, origin_y, width, height, arc_start, arc_end)
12
+ draw = Renderer.instance.draw
13
+
14
+ draw.push
15
+ draw.stroke_width(@width)
16
+ draw.stroke(@color)
17
+ draw.fill('transparent')
18
+ draw.ellipse(origin_x, origin_y, width, height, arc_start, arc_end)
19
+ draw.pop
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gruff
4
+ # @private
5
+ class Renderer::Line
6
+ EPSILON = 0.001
7
+
8
+ def initialize(color:, width: nil, shadow_color: nil)
9
+ @color = color
10
+ @width = width
11
+ @shadow_color = shadow_color
12
+ end
13
+
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)
22
+ # FIXME(uwe): Workaround for Issue #66
23
+ # https://github.com/topfunky/gruff/issues/66
24
+ # https://github.com/rmagick/rmagick/issues/82
25
+ # Remove if the issue gets fixed.
26
+ unless defined?(JRUBY_VERSION)
27
+ start_x += EPSILON
28
+ end_x += EPSILON
29
+ start_y += EPSILON
30
+ end_y += EPSILON
31
+ end
32
+
33
+ draw = Renderer.instance.draw
34
+
35
+ draw.push
36
+ draw.stroke(color)
37
+ draw.fill(color)
38
+ draw.stroke_width(@width) if @width
39
+ draw.line(start_x, start_y, end_x, end_y)
40
+ draw.pop
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gruff
4
+ # @private
5
+ class Renderer::Polygon
6
+ def initialize(color:, width: 1.0, opacity: 1.0)
7
+ @color = color
8
+ @width = width
9
+ @opacity = opacity
10
+ end
11
+
12
+ def render(points)
13
+ draw = Renderer.instance.draw
14
+
15
+ draw.push
16
+ draw.stroke_width(@width)
17
+ draw.stroke(@color)
18
+ draw.fill(@color)
19
+ draw.fill_opacity(@opacity)
20
+ draw.polygon(*points)
21
+ draw.pop
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gruff
4
+ # @private
5
+ class Renderer::Polyline
6
+ def initialize(color:, width:)
7
+ @color = color
8
+ @width = width
9
+ end
10
+
11
+ def render(points)
12
+ draw = Renderer.instance.draw
13
+
14
+ draw.push
15
+ draw.stroke(@color)
16
+ draw.fill('transparent')
17
+ draw.stroke_width(@width)
18
+ draw.polyline(*points)
19
+ draw.pop
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gruff
4
+ # @private
5
+ class Renderer::Rectangle
6
+ def initialize(color: nil)
7
+ @color = color
8
+ end
9
+
10
+ def render(upper_left_x, upper_left_y, lower_right_x, lower_right_y)
11
+ draw = Renderer.instance.draw
12
+
13
+ draw.push
14
+ draw.stroke('transparent')
15
+ draw.fill(@color) if @color
16
+ draw.rectangle(upper_left_x, upper_left_y, lower_right_x, lower_right_y)
17
+ draw.pop
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module Gruff
6
+ # @private
7
+ class Renderer
8
+ include Singleton
9
+
10
+ attr_accessor :draw, :image, :scale, :text_renderers
11
+
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)
17
+
18
+ image = Renderer.instance.background(columns, rows, scale, theme_options)
19
+
20
+ Renderer.instance.draw = draw
21
+ Renderer.instance.scale = scale
22
+ Renderer.instance.image = image
23
+ Renderer.instance.text_renderers = []
24
+ end
25
+
26
+ def self.setup_transparent_background(columns, rows)
27
+ image = Renderer.instance.render_transparent_background(columns, rows)
28
+ Renderer.instance.image = image
29
+ end
30
+
31
+ def self.background_image=(image)
32
+ Renderer.instance.image = image
33
+ end
34
+
35
+ def self.font=(font)
36
+ draw = Renderer.instance.draw
37
+ draw.font = font if font
38
+ end
39
+
40
+ def self.finish
41
+ draw = Renderer.instance.draw
42
+ image = Renderer.instance.image
43
+
44
+ draw.draw(image)
45
+
46
+ Renderer.instance.text_renderers.each do |renderer|
47
+ renderer.render(renderer.width, renderer.height, renderer.x, renderer.y, renderer.gravity)
48
+ end
49
+ end
50
+
51
+ def background(columns, rows, scale, theme_options)
52
+ case theme_options[:background_colors]
53
+ when Array
54
+ gradated_background(columns, rows, *theme_options[:background_colors][0..1], theme_options[:background_direction])
55
+ when String
56
+ solid_background(columns, rows, theme_options[:background_colors])
57
+ else
58
+ image_background(scale, *theme_options[:background_image])
59
+ end
60
+ end
61
+
62
+ # Make a new image at the current size with a solid +color+.
63
+ def solid_background(columns, rows, color)
64
+ Magick::Image.new(columns, rows) do
65
+ self.background_color = color
66
+ end
67
+ end
68
+
69
+ # Use with a theme definition method to draw a gradated background.
70
+ def gradated_background(columns, rows, top_color, bottom_color, direct = :top_bottom)
71
+ gradient_fill = begin
72
+ case direct
73
+ when :bottom_top
74
+ Magick::GradientFill.new(0, 0, 100, 0, bottom_color, top_color)
75
+ when :left_right
76
+ Magick::GradientFill.new(0, 0, 0, 100, top_color, bottom_color)
77
+ when :right_left
78
+ Magick::GradientFill.new(0, 0, 0, 100, bottom_color, top_color)
79
+ when :topleft_bottomright
80
+ Magick::GradientFill.new(0, 100, 100, 0, top_color, bottom_color)
81
+ when :topright_bottomleft
82
+ Magick::GradientFill.new(0, 0, 100, 100, bottom_color, top_color)
83
+ else
84
+ Magick::GradientFill.new(0, 0, 100, 0, top_color, bottom_color)
85
+ end
86
+ end
87
+
88
+ image = Magick::Image.new(columns, rows, gradient_fill)
89
+ @gradated_background_retry_count = 0
90
+
91
+ image
92
+ rescue StandardError => e
93
+ @gradated_background_retry_count ||= 0
94
+ GC.start
95
+
96
+ if @gradated_background_retry_count < 3
97
+ @gradated_background_retry_count += 1
98
+ gradated_background(columns, rows, top_color, bottom_color, direct)
99
+ else
100
+ raise e
101
+ end
102
+ end
103
+
104
+ # Use with a theme to use an image (800x600 original) background.
105
+ def image_background(scale, image_path)
106
+ image = Magick::Image.read(image_path)
107
+ if scale != 1.0
108
+ image[0].resize!(scale) # TODO: Resize with new scale (crop if necessary for wide graph)
109
+ end
110
+ image[0]
111
+ end
112
+
113
+ # Use with a theme to make a transparent background
114
+ def render_transparent_background(columns, rows)
115
+ Magick::Image.new(columns, rows) do
116
+ self.background_color = 'transparent'
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gruff
4
+ # @private
5
+ class Renderer::Text
6
+ using Magick::GruffAnnotate
7
+
8
+ def initialize(text, font:, size:, color:, weight: Magick::NormalWeight, rotation: nil)
9
+ @text = text.to_s
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
27
+ end
28
+
29
+ def render(width, height, x, y, gravity = Magick::NorthGravity)
30
+ draw = Renderer.instance.draw
31
+ image = Renderer.instance.image
32
+ scale = Renderer.instance.scale
33
+
34
+ draw.rotation = @rotation if @rotation
35
+ draw.fill = @font_color
36
+ draw.stroke = 'transparent'
37
+ draw.font = @font if @font
38
+ draw.font_weight = @font_weight
39
+ draw.pointsize = @font_size * scale
40
+ draw.gravity = gravity
41
+ draw.annotate_scaled(image,
42
+ width, height,
43
+ x, y,
44
+ @text, scale)
45
+ draw.rotation = -@rotation if @rotation
46
+ end
47
+
48
+ def self.metrics(text, size, font_weight = Magick::NormalWeight)
49
+ draw = Renderer.instance.draw
50
+ image = Renderer.instance.image
51
+
52
+ draw.font_weight = font_weight
53
+ draw.pointsize = size
54
+ draw.get_type_metrics(image, text.to_s)
55
+ end
56
+ end
57
+ end
@@ -1,65 +1,50 @@
1
- require File.dirname(__FILE__) + '/base'
1
+ # frozen_string_literal: true
2
2
 
3
- # Here's how to set up an XY Scatter Chart
4
3
  #
5
- # g = Gruff::Scatter.new(800)
6
- # g.data(:apples, [1,2,3,4], [4,3,2,1])
7
- # g.data('oranges', [5,7,8], [4,1,7])
8
- # g.write('test/output/scatter.png')
9
- #
4
+ # Here's how to set up a Gruff::Scatter.
5
+ #
6
+ # g = Gruff::Scatter.new(800)
7
+ # g.data :apples, [1,2,3,4], [4,3,2,1]
8
+ # g.data 'oranges', [5,7,8], [4,1,7]
9
+ # g.write('scatter.png')
10
10
  #
11
11
  class Gruff::Scatter < Gruff::Base
12
-
13
12
  # Maximum X Value. The value will get overwritten by the max in the
14
- # datasets.
15
- attr_accessor :maximum_x_value
16
-
17
- # Minimum X Value. The value will get overwritten by the min in the
18
- # datasets.
19
- attr_accessor :minimum_x_value
20
-
21
- # The number of vertical lines shown for reference
22
- attr_accessor :marker_x_count
23
-
24
- #~ # Draw a dashed horizontal line at the given y value
25
- #~ attr_accessor :baseline_y_value
26
-
27
- #~ # Color of the horizontal baseline
28
- #~ attr_accessor :baseline_y_color
29
-
30
- #~ # Draw a dashed horizontal line at the given y value
31
- #~ attr_accessor :baseline_x_value
32
-
33
- #~ # Color of the horizontal baseline
34
- #~ attr_accessor :baseline_x_color
35
-
36
- # Attributes to allow customising the size of the points
37
- attr_accessor :circle_radius
38
- attr_accessor :stroke_width
39
-
40
- # Allow disabling the significant rounding when labeling the X axis
41
- # This is useful when working with a small range of high values (for example, a date range of months, while seconds as units)
42
- attr_accessor :disable_significant_rounding_x_axis
43
-
44
- # Allow enabling vertical lines. When you have a lot of data, they can work great
45
- attr_accessor :enable_vertical_line_markers
46
-
47
- # Allow using vertical labels in the X axis (and setting the label margin)
48
- attr_accessor :x_label_margin
49
- attr_accessor :use_vertical_x_labels
50
-
51
- # Allow passing lambdas to format labels
52
- attr_accessor :y_axis_label_format
53
- attr_accessor :x_axis_label_format
54
-
55
-
56
- # Gruff::Scatter takes the same parameters as the Gruff::Line graph
57
- #
58
- # ==== Example
59
- #
60
- # g = Gruff::Scatter.new
61
- #
62
- def initialize(*)
13
+ # datasets.
14
+ attr_writer :maximum_x_value
15
+
16
+ # Minimum X Value. The value will get overwritten by the min in the
17
+ # datasets.
18
+ attr_writer :minimum_x_value
19
+
20
+ # The number of vertical lines shown for reference.
21
+ attr_writer :marker_x_count
22
+
23
+ # Attributes to allow customising the size of the points.
24
+ attr_writer :circle_radius
25
+ attr_writer :stroke_width
26
+
27
+ # Allow disabling the significant rounding when labeling the X axis.
28
+ # This is useful when working with a small range of high values (for example, a date range of months, while seconds as units).
29
+ attr_writer :disable_significant_rounding_x_axis
30
+
31
+ # Allow enabling vertical lines. When you have a lot of data, they can work great.
32
+ attr_writer :enable_vertical_line_markers
33
+
34
+ # Allow using vertical labels in the X axis (and setting the label margin).
35
+ attr_writer :x_label_margin
36
+ attr_writer :use_vertical_x_labels
37
+
38
+ # Allow passing lambdas to format labels.
39
+ attr_writer :y_axis_label_format
40
+ attr_writer :x_axis_label_format
41
+
42
+ def initialize_store
43
+ @store = Gruff::Store.new(Gruff::Store::XYData)
44
+ end
45
+ private :initialize_store
46
+
47
+ def initialize_ivars
63
48
  super
64
49
 
65
50
  @baseline_x_color = @baseline_y_color = 'red'
@@ -75,62 +60,30 @@ class Gruff::Scatter < Gruff::Base
75
60
  @x_label_margin = nil
76
61
  @y_axis_label_format = nil
77
62
  end
63
+ private :initialize_ivars
78
64
 
79
- def setup_drawing
80
- # TODO Need to get x-axis labels working. Current behavior will be to not allow.
81
- @labels = {}
82
-
65
+ def draw
83
66
  super
67
+ return unless data_given?
84
68
 
85
- # Translate our values so that we can use the base methods for drawing
86
- # the standard chart stuff
87
- @column_count = @x_spread
88
- end
69
+ # Check to see if more than one datapoint was given. NaN can result otherwise.
70
+ @x_increment = (@x_spread > 1) ? (@graph_width / (@x_spread - 1).to_f) : @graph_width
89
71
 
90
- def draw
91
- super
92
- return unless @has_data
93
-
94
- # Check to see if more than one datapoint was given. NaN can result otherwise.
95
- @x_increment = (@column_count > 1) ? (@graph_width / (@column_count - 1).to_f) : @graph_width
96
-
97
- #~ if (defined?(@norm_y_baseline)) then
98
- #~ level = @graph_top + (@graph_height - @norm_baseline * @graph_height)
99
- #~ @d = @d.push
100
- #~ @d.stroke_color @baseline_color
101
- #~ @d.fill_opacity 0.0
102
- #~ @d.stroke_dasharray(10, 20)
103
- #~ @d.stroke_width 5
104
- #~ @d.line(@graph_left, level, @graph_left + @graph_width, level)
105
- #~ @d = @d.pop
106
- #~ end
107
-
108
- #~ if (defined?(@norm_x_baseline)) then
109
-
110
- #~ end
111
-
112
- @norm_data.each do |data_row|
113
- data_row[DATA_VALUES_INDEX].each_with_index do |data_point, index|
114
- x_value = data_row[DATA_VALUES_X_INDEX][index]
115
- next if data_point.nil? || x_value.nil?
72
+ store.norm_data.each do |data_row|
73
+ data_row.coordinates.each do |x_value, y_value|
74
+ next if y_value.nil? || x_value.nil?
116
75
 
117
76
  new_x = get_x_coord(x_value, @graph_width, @graph_left)
118
- new_y = @graph_top + (@graph_height - data_point * @graph_height)
77
+ new_y = @graph_top + (@graph_height - y_value * @graph_height)
119
78
 
120
79
  # Reset each time to avoid thin-line errors
121
- @d = @d.stroke data_row[DATA_COLOR_INDEX]
122
- @d = @d.fill data_row[DATA_COLOR_INDEX]
123
- @d = @d.stroke_opacity 1.0
124
- @d = @d.stroke_width @stroke_width || clip_value_if_greater_than(@columns / (@norm_data.first[1].size * 4), 5.0)
125
-
126
- circle_radius = @circle_radius || clip_value_if_greater_than(@columns / (@norm_data.first[1].size * 2.5), 5.0)
127
- @d = @d.circle(new_x, new_y, new_x - circle_radius, new_y)
80
+ stroke_width = @stroke_width || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 4), 5.0)
81
+ circle_radius = @circle_radius || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 2.5), 5.0)
82
+ Gruff::Renderer::Circle.new(color: data_row.color, width: stroke_width).render(new_x, new_y, new_x - circle_radius, new_y)
128
83
  end
129
84
  end
130
-
131
- @d.draw(@base_image)
132
85
  end
133
-
86
+
134
87
  # The first parameter is the name of the dataset. The next two are the
135
88
  # x and y axis data points contain in their own array in that respective
136
89
  # order. The final parameter is the color.
@@ -141,95 +94,83 @@ class Gruff::Scatter < Gruff::Base
141
94
  # If the color argument is nil, the next color from the default theme will
142
95
  # be used.
143
96
  #
144
- # NOTE: If you want to use a preset theme, you must set it before calling
145
- # data().
97
+ # @note If you want to use a preset theme, you must set it before calling {#data}.
146
98
  #
147
- # ==== Parameters
148
- # name:: String or Symbol containing the name of the dataset.
149
- # x_data_points:: An Array of of x-axis data points.
150
- # y_data_points:: An Array of of y-axis data points.
151
- # color:: The hex string for the color of the dataset. Defaults to nil.
99
+ # @param name [String, Symbol] containing the name of the dataset.
100
+ # @param x_data_points [Array] An Array of of x-axis data points.
101
+ # @param y_data_points [Array] An Array of of y-axis data points.
102
+ # @param color [String] The hex string for the color of the dataset. Defaults to nil.
152
103
  #
153
- # ==== Exceptions
154
- # Data points contain nil values::
104
+ #
105
+ # @raise [ArgumentError] Data points contain nil values.
155
106
  # This error will get raised if either the x or y axis data points array
156
- # contains a <tt>nil</tt> value. The graph will not make an assumption
157
- # as how to graph <tt>nil</tt>
158
- # x_data_points is empty::
107
+ # contains a +nil+ value. The graph will not make an assumption
108
+ # as how to graph +nil+.
109
+ # @raise [ArgumentError] +x_data_points+ is empty.
159
110
  # This error is raised when the array for the x-axis points are empty
160
- # y_data_points is empty::
161
- # This error is raised when the array for the y-axis points are empty
162
- # x_data_points.length != y_data_points.length::
163
- # Error means that the x and y axis point arrays do not match in length
111
+ # @raise [ArgumentError] +y_data_points+ is empty.
112
+ # This error is raised when the array for the y-axis points are empty.
113
+ # @raise [ArgumentError] +x_data_points.length != y_data_points.length+.
114
+ # Error means that the x and y axis point arrays do not match in length.
164
115
  #
165
- # ==== Examples
166
- # g = Gruff::Scatter.new
167
- # g.data(:apples, [1,2,3], [3,2,1])
168
- # g.data('oranges', [1,1,1], [2,3,4])
169
- # g.data('bitter_melon', [3,5,6], [6,7,8], '#000000')
116
+ # @example
117
+ # g = Gruff::Scatter.new
118
+ # g.data(:apples, [1,2,3], [3,2,1])
119
+ # g.data('oranges', [1,1,1], [2,3,4])
120
+ # g.data('bitter_melon', [3,5,6], [6,7,8], '#000000')
170
121
  #
171
- def data(name, x_data_points=[], y_data_points=[], color=nil)
172
-
122
+ def data(name, x_data_points = [], y_data_points = [], color = nil)
123
+ # make sure it's an array
124
+ x_data_points = Array(x_data_points)
125
+ y_data_points = Array(y_data_points)
126
+
173
127
  raise ArgumentError, 'Data Points contain nil Value!' if x_data_points.include?(nil) || y_data_points.include?(nil)
174
128
  raise ArgumentError, 'x_data_points is empty!' if x_data_points.empty?
175
129
  raise ArgumentError, 'y_data_points is empty!' if y_data_points.empty?
176
130
  raise ArgumentError, 'x_data_points.length != y_data_points.length!' if x_data_points.length != y_data_points.length
177
-
178
- # Call the existing data routine for the y axis data
179
- super(name, y_data_points, color)
180
-
181
- #append the x data to the last entry that was just added in the @data member
182
- last_elem = @data.length()-1
183
- @data[last_elem] << x_data_points
184
-
185
- if @maximum_x_value.nil? && @minimum_x_value.nil?
186
- @maximum_x_value = @minimum_x_value = x_data_points.first
187
- end
188
-
189
- @maximum_x_value = x_data_points.max > @maximum_x_value ?
190
- x_data_points.max : @maximum_x_value
191
- @minimum_x_value = x_data_points.min < @minimum_x_value ?
192
- x_data_points.min : @minimum_x_value
131
+
132
+ # Call the existing data routine for the x/y axis data
133
+ store.add(name, y_data_points, color, x_data_points)
134
+ end
135
+
136
+ alias dataxy data
137
+
138
+ private
139
+
140
+ def setup_data
141
+ # Update the global min/max values for the x data
142
+ @maximum_x_value ||= store.max_x
143
+ @minimum_x_value ||= store.min_x
144
+
145
+ super
146
+ end
147
+
148
+ def setup_drawing
149
+ # TODO: Need to get x-axis labels working. Current behavior will be to not allow.
150
+ @labels = {}
151
+
152
+ super
193
153
  end
194
-
195
- protected
196
-
154
+
197
155
  def calculate_spread #:nodoc:
198
156
  super
199
157
  @x_spread = @maximum_x_value.to_f - @minimum_x_value.to_f
200
158
  @x_spread = @x_spread > 0 ? @x_spread : 1
201
159
  end
202
-
203
- def normalize(force=nil)
204
- if @norm_data.nil? || force
205
- @norm_data = []
206
- return unless @has_data
207
-
208
- @data.each do |data_row|
209
- norm_data_points = [data_row[DATA_LABEL_INDEX]]
210
- norm_data_points << data_row[DATA_VALUES_INDEX].map do |r|
211
- (r.to_f - @minimum_value.to_f) / @spread
212
- end
213
- norm_data_points << data_row[DATA_COLOR_INDEX]
214
- norm_data_points << data_row[DATA_VALUES_X_INDEX].map do |r|
215
- (r.to_f - @minimum_x_value.to_f) / @x_spread
216
- end
217
- @norm_data << norm_data_points
218
- end
219
- end
220
- #~ @norm_y_baseline = (@baseline_y_value.to_f / @maximum_value.to_f) if @baseline_y_value
221
- #~ @norm_x_baseline = (@baseline_x_value.to_f / @maximum_x_value.to_f) if @baseline_x_value
160
+
161
+ def normalize
162
+ return unless data_given?
163
+
164
+ store.normalize(minimum_x: @minimum_x_value, spread_x: @x_spread, minimum_y: minimum_value, spread_y: @spread)
222
165
  end
223
-
166
+
224
167
  def draw_line_markers
225
168
  # do all of the stuff for the horizontal lines on the y-axis
226
169
  super
227
170
  return if @hide_line_markers
228
-
229
- @d = @d.stroke_antialias false
230
171
 
231
172
  if @x_axis_increment.nil?
232
- # TODO Do the same for larger numbers...100, 75, 50, 25
173
+ # TODO: Do the same for larger numbers...100, 75, 50, 25
233
174
  if @marker_x_count.nil?
234
175
  (3..7).each do |lines|
235
176
  if @x_spread % lines == 0.0
@@ -244,51 +185,40 @@ protected
244
185
  @x_increment = significant(@x_increment)
245
186
  end
246
187
  else
247
- # TODO Make this work for negative values
248
- @maximum_x_value = [@maximum_value.ceil, @x_axis_increment].max
188
+ # TODO: Make this work for negative values
189
+ @maximum_x_value = [maximum_value.ceil, @x_axis_increment].max
249
190
  @minimum_x_value = @minimum_x_value.floor
250
191
  calculate_spread
251
- normalize(true)
252
-
253
- @marker_count = (@x_spread / @x_axis_increment).to_i
192
+ normalize
193
+
194
+ self.marker_count = (@x_spread / @x_axis_increment).to_i
254
195
  @x_increment = @x_axis_increment
255
196
  end
256
- @increment_x_scaled = @graph_width.to_f / (@x_spread / @x_increment)
197
+ increment_x_scaled = @graph_width.to_f / (@x_spread / @x_increment)
257
198
 
258
199
  # Draw vertical line markers and annotate with numbers
259
200
  (0..@marker_x_count).each do |index|
260
-
261
- # TODO Fix the vertical lines, and enable them by default. Not pretty when they don't match up with top y-axis line
201
+ # TODO: Fix the vertical lines, and enable them by default. Not pretty when they don't match up with top y-axis line
262
202
  if @enable_vertical_line_markers
263
- x = @graph_left + @graph_width - index.to_f * @increment_x_scaled
264
- @d = @d.stroke(@marker_color)
265
- @d = @d.stroke_width 1
266
- @d = @d.line(x, @graph_top, x, @graph_bottom)
203
+ x = @graph_left + @graph_width - index.to_f * increment_x_scaled
204
+
205
+ line_renderer = Gruff::Renderer::Line.new(color: @marker_color, shadow_color: @marker_shadow_color)
206
+ line_renderer.render(x, @graph_top, x, @graph_bottom)
267
207
  end
268
208
 
269
209
  unless @hide_line_numbers
270
210
  marker_label = index * @x_increment + @minimum_x_value.to_f
271
211
  y_offset = @graph_bottom + (@x_label_margin || LABEL_MARGIN)
272
- x_offset = get_x_coord(index.to_f, @increment_x_scaled, @graph_left)
273
-
274
- @d.fill = @font_color
275
- @d.font = @font if @font
276
- @d.stroke('transparent')
277
- @d.pointsize = scale_fontsize(@marker_font_size)
278
- @d.gravity = NorthGravity
279
- @d.rotation = -90.0 if @use_vertical_x_labels
280
- @d = @d.annotate_scaled(@base_image,
281
- 1.0, 1.0,
282
- x_offset, y_offset,
283
- vertical_label(marker_label, @x_increment), @scale)
284
- @d.rotation = 90.0 if @use_vertical_x_labels
212
+ x_offset = get_x_coord(index.to_f, increment_x_scaled, @graph_left)
213
+
214
+ label = vertical_label(marker_label, @x_increment)
215
+ rotation = -90.0 if @use_vertical_x_labels
216
+ text_renderer = Gruff::Renderer::Text.new(label, font: @font, size: @marker_font_size, color: @font_color, rotation: rotation)
217
+ text_renderer.add_to_render_queue(1.0, 1.0, x_offset, y_offset)
285
218
  end
286
219
  end
287
-
288
- @d = @d.stroke_antialias true
289
220
  end
290
221
 
291
-
292
222
  def label(value, increment)
293
223
  if @y_axis_label_format
294
224
  @y_axis_label_format.call(value)
@@ -304,11 +234,8 @@ protected
304
234
  label(value, increment)
305
235
  end
306
236
  end
307
-
308
- private
309
-
237
+
310
238
  def get_x_coord(x_data_point, width, offset) #:nodoc:
311
239
  x_data_point * width + offset
312
240
  end
313
-
314
- end # end Gruff::Scatter
241
+ end