rubyplot 0.0.1 → 0.1.pre.a1

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 (62) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +133 -0
  5. data/.travis.yml +18 -0
  6. data/CHANGELOG.md +9 -0
  7. data/CONTRIBUTING.md +48 -0
  8. data/Gemfile +6 -0
  9. data/README.md +47 -0
  10. data/Rakefile +32 -0
  11. data/ext/grruby/extconf.rb +6 -0
  12. data/ext/grruby/grruby.c +1163 -0
  13. data/ext/grruby/grruby.h +135 -0
  14. data/lib/rubyplot.rb +30 -1
  15. data/lib/rubyplot/artist.rb +13 -0
  16. data/lib/rubyplot/artist/axes.rb +328 -0
  17. data/lib/rubyplot/artist/axis.rb +3 -0
  18. data/lib/rubyplot/artist/axis/base.rb +34 -0
  19. data/lib/rubyplot/artist/axis/x_axis.rb +35 -0
  20. data/lib/rubyplot/artist/axis/y_axis.rb +40 -0
  21. data/lib/rubyplot/artist/base.rb +14 -0
  22. data/lib/rubyplot/artist/circle.rb +28 -0
  23. data/lib/rubyplot/artist/figure.rb +90 -0
  24. data/lib/rubyplot/artist/legend.rb +59 -0
  25. data/lib/rubyplot/artist/legend_box.rb +89 -0
  26. data/lib/rubyplot/artist/line2d.rb +24 -0
  27. data/lib/rubyplot/artist/plot.rb +9 -0
  28. data/lib/rubyplot/artist/plot/area.rb +38 -0
  29. data/lib/rubyplot/artist/plot/bar.rb +69 -0
  30. data/lib/rubyplot/artist/plot/bar_type.rb +31 -0
  31. data/lib/rubyplot/artist/plot/base.rb +67 -0
  32. data/lib/rubyplot/artist/plot/bubble.rb +41 -0
  33. data/lib/rubyplot/artist/plot/line.rb +61 -0
  34. data/lib/rubyplot/artist/plot/multi_bars.rb +75 -0
  35. data/lib/rubyplot/artist/plot/multi_stacked_bar.rb +78 -0
  36. data/lib/rubyplot/artist/plot/scatter.rb +29 -0
  37. data/lib/rubyplot/artist/plot/stacked_bar.rb +69 -0
  38. data/lib/rubyplot/artist/polygon.rb +21 -0
  39. data/lib/rubyplot/artist/rectangle.rb +39 -0
  40. data/lib/rubyplot/artist/text.rb +49 -0
  41. data/lib/rubyplot/artist/tick.rb +3 -0
  42. data/lib/rubyplot/artist/tick/base.rb +35 -0
  43. data/lib/rubyplot/artist/tick/x_tick.rb +25 -0
  44. data/lib/rubyplot/artist/tick/y_tick.rb +24 -0
  45. data/lib/rubyplot/backend.rb +2 -0
  46. data/lib/rubyplot/backend/gr_wrapper.rb +7 -0
  47. data/lib/rubyplot/backend/magick_wrapper.rb +141 -0
  48. data/lib/rubyplot/color.rb +992 -0
  49. data/lib/rubyplot/figure.rb +2 -0
  50. data/lib/rubyplot/spi.rb +8 -0
  51. data/lib/rubyplot/subplot.rb +4 -0
  52. data/lib/rubyplot/themes.rb +47 -0
  53. data/lib/rubyplot/utils.rb +14 -0
  54. data/lib/rubyplot/version.rb +1 -1
  55. data/rubyplot.gemspec +10 -0
  56. data/spec/axes_spec.rb +477 -0
  57. data/spec/figure_spec.rb +12 -0
  58. data/spec/spec_helper.rb +62 -0
  59. data/spec/spi/multi_plot_graph_spec.rb +33 -0
  60. data/spec/spi/single_plot_graph_spec.rb +227 -0
  61. data/spec/spi/subplots_spec.rb +55 -0
  62. metadata +166 -8
@@ -0,0 +1,3 @@
1
+ require_relative 'axis/base'
2
+ require_relative 'axis/x_axis'
3
+ require_relative 'axis/y_axis'
@@ -0,0 +1,34 @@
1
+ module Rubyplot
2
+ module Artist
3
+ class Axis
4
+ class Base
5
+ # Length in pixels of the arrow after the last major tick.
6
+ FINISH_ARROW_LENGTH = 10.0
7
+
8
+ attr_reader :label, :ticks, :major_ticks_count
9
+ attr_reader :abs_x1, :abs_x2, :abs_y1, :abs_y2, :backend, :length
10
+ attr_reader :stroke_width, :major_ticks
11
+ attr_accessor :title, :min_val, :max_val
12
+
13
+ def initialize axes
14
+ @axes = axes
15
+ @title = ''
16
+ @min_val = nil
17
+ @max_val = nil
18
+ @stroke_width = 1.0
19
+ @major_ticks_count = 5
20
+ @x_ticks = nil
21
+ @texts = []
22
+ @lines = []
23
+ end
24
+
25
+ def draw
26
+ configure_title
27
+ @lines.each(&:draw)
28
+ @texts.each(&:draw)
29
+ end
30
+
31
+ end # class Base
32
+ end # class Axis
33
+ end # class Artist
34
+ end # module Rubyplot
@@ -0,0 +1,35 @@
1
+ require_relative 'base'
2
+
3
+ module Rubyplot
4
+ module Artist
5
+ class XAxis < Axis::Base
6
+ def initialize axes
7
+ super
8
+ @abs_x1 = @axes.origin[0]
9
+ @abs_x2 = @axes.abs_x + @axes.width - @axes.y_axis_margin
10
+ @major_ticks_distance = (@abs_x2 - @abs_x1) / @major_ticks_count
11
+ @length = (@abs_x2 - @abs_x1).abs
12
+ configure_axis_line
13
+ end
14
+
15
+ private
16
+
17
+ def configure_axis_line
18
+ @lines << Rubyplot::Artist::Line2D.new(
19
+ self, abs_x1: @abs_x1, abs_y1: @axes.origin[1], abs_x2: @abs_x2, abs_y2: @axes.origin[1],
20
+ stroke_width: @stroke_width
21
+ )
22
+ end
23
+
24
+ def configure_title
25
+ @texts << Rubyplot::Artist::Text.new(
26
+ @title,
27
+ self,
28
+ pointsize: @axes.marker_font_size,
29
+ abs_y: @axes.origin[1] + 20,
30
+ abs_x: @axes.origin[0] + (@abs_x2 - @abs_x1)/2
31
+ )
32
+ end
33
+ end # class XAxis
34
+ end # class Artist
35
+ end # module Rubyplot
@@ -0,0 +1,40 @@
1
+ module Rubyplot
2
+ module Artist
3
+ class YAxis < Axis::Base
4
+ def initialize(*)
5
+ super
6
+ @abs_x1 = @axes.origin[0]
7
+ @abs_y1 = @axes.origin[1]
8
+ @abs_x2 = @axes.origin[0]
9
+ @abs_y2 = @axes.origin[1] - (@axes.height - @axes.x_axis_margin)
10
+ @y_ticks = []
11
+ @length = (@abs_y1 - @abs_y2).abs
12
+ configure_axis_line
13
+ end
14
+
15
+ private
16
+
17
+ def configure_axis_line
18
+ @lines << Rubyplot::Artist::Line2D.new(
19
+ self,
20
+ abs_x1: @abs_x1,
21
+ abs_y1: @abs_y1,
22
+ abs_x2: @abs_x2,
23
+ abs_y2: @abs_y2,
24
+ stroke_width: @stroke_width
25
+ )
26
+ end
27
+
28
+ def configure_title
29
+ @texts << Rubyplot::Artist::Text.new(
30
+ @title,
31
+ self,
32
+ rotation: -90.0,
33
+ abs_x: @axes.origin[0] - 10,
34
+ abs_y: (@abs_y1 - @abs_y2) / 2,
35
+ pointsize: @axes.marker_font_size
36
+ )
37
+ end
38
+ end # class YAxis
39
+ end # class Artist
40
+ end # module Rubyplot
@@ -0,0 +1,14 @@
1
+ module Rubyplot
2
+ module Artist
3
+ class Base
4
+ # Absolute X co-ordinate of this Aritist on the canvas.
5
+ attr_reader :abs_x
6
+ # Absolute Y co-ordinate of this Aritist on the canvas.
7
+ attr_reader :abs_y
8
+ def initialize(abs_x, abs_y)
9
+ @abs_x = abs_x
10
+ @abs_y = abs_y
11
+ end
12
+ end # class Base
13
+ end # module Artist
14
+ end # module Rubyplot
@@ -0,0 +1,28 @@
1
+ module Rubyplot
2
+ module Artist
3
+ class Circle < Base
4
+ # rubocop:disable Metrics/ParameterLists
5
+ def initialize(owner, abs_x:, abs_y:, radius:, stroke_opacity: 0.0,
6
+ color: :default, stroke_width:)
7
+ super(abs_x, abs_y)
8
+ @owner = owner
9
+ @radius = radius
10
+ @stroke_width = stroke_width
11
+ @stroke_opacity = stroke_opacity
12
+ @color = color
13
+ end
14
+ # rubocop:enable Metrics/ParameterLists
15
+
16
+ def draw
17
+ Rubyplot.backend.draw_circle(
18
+ x: @abs_x,
19
+ y: @abs_y,
20
+ radius: @radius,
21
+ stroke_opacity: @stroke_opacity,
22
+ stroke_width: @stroke_width,
23
+ color: Rubyplot::Color::COLOR_INDEX[@color]
24
+ )
25
+ end
26
+ end # class Circle
27
+ end # module Artist
28
+ end # module Rubyplot
@@ -0,0 +1,90 @@
1
+ module Rubyplot
2
+ module Artist
3
+ class Figure < Artist::Base
4
+ DEFAULT_TARGET_WIDTH = 800
5
+
6
+ # Title on the figure.
7
+ attr_reader :title
8
+ # Number of Axes objects in the rows.
9
+ attr_reader :nrows
10
+ # Number of Axes objects in the cols.
11
+ attr_reader :ncols
12
+ # Total width of the Figure in pixels.
13
+ attr_reader :width
14
+ # Total height of the Figure in pixels.
15
+ attr_reader :height
16
+ # Ratio of the total height that is the spacing between the top of the
17
+ # Figure and the top of the Axes.
18
+ attr_reader :top_spacing
19
+ # Ratio of the total width that is the spacing between the left of the
20
+ # Figure and the left of the Axes.
21
+ attr_reader :left_spacing
22
+ # Ratio of the total width that is the spacing between the right of the
23
+ # Figure and the right of the Axes.
24
+ attr_reader :right_spacing
25
+ # Ratio of the total width that is the spacing between the bottom of the
26
+ # Figure and the bottom of the Axes.
27
+ attr_reader :bottom_spacing
28
+ attr_reader :theme_options
29
+ attr_reader :marker_color
30
+ attr_reader :font_color
31
+
32
+ # Initialize a Rubyplot::Artist::Figure object.
33
+ # @param height [Integer] nil Height in pixels of the complete Figure.
34
+ # @param width [Integer] nil Width in pixels of the complete Figure.
35
+ def initialize(height: nil, width: nil)
36
+ super(0, 0)
37
+ @title = ''
38
+ @nrows = 1
39
+ @ncols = 1
40
+ @width = width || DEFAULT_TARGET_WIDTH
41
+ @height = height || @width * 0.75
42
+ @top_spacing = 0.05
43
+ @bottom_spacing = 0.05
44
+ @left_spacing = 0.05
45
+ @right_spacing = 0.05
46
+ @subplots = nil
47
+ @n = 0
48
+ setup_default_theme
49
+ add_subplots @nrows, @ncols
50
+ end
51
+
52
+ def add_subplots(nrows, ncols)
53
+ @subplots = Array.new(nrows) { Array.new(ncols) { nil } }
54
+ end
55
+
56
+ def add_subplot(nrow, ncol)
57
+ @subplots[nrow][ncol] = Rubyplot::Artist::Axes.new(self)
58
+ @subplots[nrow][ncol]
59
+ end
60
+
61
+ # Draw on a canvas and output to a file.
62
+ #
63
+ # @param output [TrueClass, FalseClass] true Whether to output to file or not.
64
+ def write(file_name, output: true)
65
+ Rubyplot.backend.set_base_image_gradient(
66
+ Rubyplot::Color::COLOR_INDEX[@theme_options[:background_colors][0]],
67
+ Rubyplot::Color::COLOR_INDEX[@theme_options[:background_colors][1]],
68
+ @width,
69
+ @height,
70
+ @theme_options[:background_direction]
71
+ )
72
+ @subplots.each { |i| i.each(&:draw) }
73
+ Rubyplot.backend.write(file_name) if output
74
+ end
75
+
76
+ private
77
+
78
+ def setup_default_theme
79
+ defaults = {
80
+ marker_color: :white,
81
+ font_color: :black,
82
+ background_image: nil
83
+ }
84
+ @theme_options = defaults.merge Themes::CLASSIC_WHITE
85
+ @marker_color = @theme_options[:marker_color]
86
+ @font_color = @theme_options[:font_color] || @marker_color
87
+ end
88
+ end # class Figure
89
+ end # module Artist
90
+ end # module Rubyplot
@@ -0,0 +1,59 @@
1
+ module Rubyplot
2
+ module Artist
3
+ class Legend < Artist::Base
4
+ TOP_MARGIN = 2.5
5
+ BOTTOM_MARGIN = 2.5
6
+ # Space between the color box and the legend text.
7
+ BOX_AND_TEXT_SPACE = 5.0
8
+ attr_reader :legend_box_size, :font, :font_size, :font_color
9
+
10
+ # rubocop:disable Metrics/ParameterLists
11
+ def initialize(legend_box, axes, text:, color:,abs_x:,abs_y:)
12
+ super(abs_x, abs_y)
13
+ @legend_box = legend_box
14
+ @axes = axes
15
+ @text = text
16
+ @color = color
17
+ @legend_box_size = @legend_box.per_legend_height -
18
+ (TOP_MARGIN + BOTTOM_MARGIN) # size of the color box of the legend.
19
+ @font_size = 20.0
20
+ @font = @axes.font
21
+ @font_color = @axes.font_color
22
+ configure_legend_color_box
23
+ configure_legend_text
24
+ end
25
+ # rubocop:enable Metrics/ParameterLists
26
+
27
+ def draw
28
+ @legend_color_box.draw
29
+ @text.draw
30
+ end
31
+
32
+ private
33
+
34
+ def configure_legend_color_box
35
+ @legend_color_box = Rubyplot::Artist::Rectangle.new(
36
+ self,
37
+ abs_x: @abs_x,
38
+ abs_y: @abs_y + TOP_MARGIN,
39
+ width: @legend_box_size,
40
+ height: @legend_box_size,
41
+ border_color: @color,
42
+ fill_color: @color
43
+ )
44
+ end
45
+
46
+ def configure_legend_text
47
+ @text = Rubyplot::Artist::Text.new(
48
+ @text,
49
+ self,
50
+ abs_x: @abs_x + @legend_box_size + BOX_AND_TEXT_SPACE,
51
+ abs_y: @legend_color_box.abs_y + @legend_box_size - 2.5,
52
+ font: @font,
53
+ color: @font_color,
54
+ pointsize: @font_size
55
+ )
56
+ end
57
+ end # class Legend
58
+ end # class Artist
59
+ end # module Rubyplot
@@ -0,0 +1,89 @@
1
+ module Rubyplot
2
+ module Artist
3
+ class LegendBox < Base
4
+ # Ratio of total height of legend box that is the top margin.
5
+ TOP_SPACING_RATIO = 0.1
6
+ # Ratio of the total height of the legend box that is the bottom margin.
7
+ BOTTOM_SPACING_RATIO = 0.1
8
+ # Ratio of the total width of the legend box that is the left margin.
9
+ LEFT_SPACING_RATIO = 0.1
10
+ # Ratio of the total width of the legend box that is the right margin.
11
+ RIGHT_SPACING_RATIO = 0.1
12
+
13
+ attr_accessor :border_color
14
+
15
+ def initialize(axes, abs_x:, abs_y:)
16
+ super(abs_x, abs_y)
17
+ @axes = axes
18
+ @border_color = :black
19
+ @legends = []
20
+ configure_dimensions
21
+ configure_legends
22
+ configure_legend_box
23
+ end
24
+
25
+ def draw
26
+ unless @legends.empty?
27
+ @bounding_box.draw
28
+ @legends.each(&:draw)
29
+ end
30
+ end
31
+
32
+ def top_margin
33
+ TOP_SPACING_RATIO * @legends_height
34
+ end
35
+
36
+ def bottom_margin
37
+ BOTTOM_SPACING_RATIO * @legends_height
38
+ end
39
+
40
+ def left_margin
41
+ LEFT_SPACING_RATIO * @legends_width
42
+ end
43
+
44
+ def right_margin
45
+ RIGHT_SPACING_RATIO * @legends_width
46
+ end
47
+
48
+ # Calculation of the height that each legend takes.
49
+ def per_legend_height
50
+ 25.0
51
+ end
52
+
53
+ private
54
+
55
+ def configure_legend_box
56
+ @bounding_box = Rubyplot::Artist::Rectangle.new(
57
+ self,
58
+ abs_x: @abs_x,
59
+ abs_y: @abs_y,
60
+ width: @width,
61
+ height: @height,
62
+ border_color: @border_color
63
+ )
64
+ end
65
+
66
+ def configure_dimensions
67
+ @legends_height = @axes.plots.size * per_legend_height
68
+ @legends_width = 0.2 * @axes.width
69
+ @height = @legends_height + top_margin + bottom_margin
70
+ @width = @legends_width + left_margin + right_margin
71
+ end
72
+
73
+ def configure_legends
74
+ @axes.plots.each_with_index do |plot, count|
75
+ next unless plot.label != ''
76
+
77
+ @legends << Rubyplot::Artist::Legend.new(
78
+ self,
79
+ @axes,
80
+ text: plot.label,
81
+ color: plot.color,
82
+ abs_x: @abs_x + left_margin,
83
+ abs_y: @abs_y + count * per_legend_height + top_margin
84
+ )
85
+ end
86
+ end
87
+ end # class LegendBox
88
+ end # module Artist
89
+ end # module Rubyplot
@@ -0,0 +1,24 @@
1
+ module Rubyplot
2
+ module Artist
3
+ class Line2D < Artist::Base
4
+ # rubocop:disable Metrics/ParameterLists
5
+ def initialize(owner,abs_x1:,abs_y1:,abs_x2:,abs_y2:,color: '#000000',
6
+ stroke_opacity: 0.0, stroke_width: 2.0)
7
+ @owner = owner
8
+ @abs_x1 = abs_x1
9
+ @abs_y1 = abs_y1
10
+ @abs_x2 = abs_x2
11
+ @abs_y2 = abs_y2
12
+ @color = color
13
+ @stroke_opacity = stroke_opacity
14
+ @stroke_width = stroke_width
15
+ end
16
+ # rubocop:enable Metrics/ParameterLists
17
+
18
+ def draw
19
+ Rubyplot.backend.draw_line(x1: @abs_x1, y1: @abs_y1, x2: @abs_x2, y2: @abs_y2,
20
+ stroke_width: @stroke_width)
21
+ end
22
+ end # class Line2D
23
+ end # class Artist
24
+ end # module Rubyplot
@@ -0,0 +1,9 @@
1
+ require_relative 'plot/base'
2
+ require_relative 'plot/scatter'
3
+ require_relative 'plot/line'
4
+ require_relative 'plot/bar'
5
+ require_relative 'plot/multi_bars'
6
+ require_relative 'plot/stacked_bar'
7
+ require_relative 'plot/multi_stacked_bar'
8
+ require_relative 'plot/bubble'
9
+ require_relative 'plot/area'
@@ -0,0 +1,38 @@
1
+ module Rubyplot
2
+ module Artist
3
+ module Plot
4
+ class Area < Artist::Plot::Base
5
+ attr_accessor :sorted_data
6
+
7
+ def initialize(*)
8
+ super
9
+ @sorted_data = true
10
+ end
11
+
12
+ def data x_values, y_values=[]
13
+ y_values = Array.new(x_values.size) { |i| i } if y_values.empty?
14
+ x_values.sort! if @sorted_data
15
+ super(x_values, y_values)
16
+ end
17
+
18
+ def draw
19
+ poly_points = []
20
+ @normalized_data[:y_values].each_with_index do |iy, idx_y|
21
+ ix = @normalized_data[:x_values][idx_y]
22
+ abs_x = ix * @axes.x_axis.length + @axes.abs_x + @axes.y_axis_margin
23
+ abs_y = (@axes.y_axis.length - iy * @axes.y_axis.length) + @axes.abs_y
24
+ poly_points << [abs_x, abs_y]
25
+ end
26
+ poly_points << [@axes.x_axis.abs_x2, @axes.origin[1] - @axes.x_axis.stroke_width]
27
+ poly_points << [@axes.origin[0], @axes.origin[1] - @axes.x_axis.stroke_width]
28
+ Rubyplot::Artist::Polygon.new(
29
+ self,
30
+ coords: poly_points,
31
+ color: @data[:color],
32
+ fill_opacity: 0.3
33
+ ).draw
34
+ end
35
+ end # class Area
36
+ end # module Plot
37
+ end # module Artist
38
+ end # module Rubyplot