rubyplot 0.0.1 → 0.1.pre.a1

Sign up to get free protection for your applications and to get access to all the features.
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